diff --git a/App.BulletPhysics/NoOpt/App.BulletPhysics.lib b/App.BulletPhysics/NoOpt/App.BulletPhysics.lib index a2b95a0..93499f2 100644 Binary files a/App.BulletPhysics/NoOpt/App.BulletPhysics.lib and b/App.BulletPhysics/NoOpt/App.BulletPhysics.lib differ diff --git a/App/App.vcxproj b/App/App.vcxproj index 2d927b5..0d2f1bd 100644 --- a/App/App.vcxproj +++ b/App/App.vcxproj @@ -140,6 +140,9 @@ bin\$(Configuration)\ obj\$(Configuration)\ + + $(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);$(CONTRIB_PATH)\boost_1_56_0\lib; + Disabled @@ -302,6 +305,11 @@ cmd /c "exit /b 0" false + + + $(CONTRIB_PATH)\boost_1_56_0\lib;$(CONTRIB_PATH)\boost_1_56_0\; + + Level1 diff --git a/App/CMakeLists.txt b/App/CMakeLists.txt index ac55046..7f219b3 100644 --- a/App/CMakeLists.txt +++ b/App/CMakeLists.txt @@ -27,8 +27,6 @@ list(APPEND SOURCES ${libwww_ROOT}/HTParse.c) set_source_files_properties(${libwww_ROOT}/HTParse.c PROPERTIES COMPILE_FLAGS "${libwww_cflags}") list(APPEND SOURCES ${libwww_ROOT}/HTString.c) set_source_files_properties(${libwww_ROOT}/HTString.c PROPERTIES COMPILE_FLAGS "${libwww_cflags}") -list(APPEND SOURCES ${libwww_ROOT}/HTTrace.c) -set_source_files_properties(${libwww_ROOT}/HTTrace.c PROPERTIES COMPILE_FLAGS "${libwww_cflags}") if(UNIX) list(APPEND SOURCES util/Unix/ProgramMemoryChecker.cpp) diff --git a/App/NoOpt/App.lastbuildstate b/App/NoOpt/App.lastbuildstate index 38f6559..656d8d7 100644 --- a/App/NoOpt/App.lastbuildstate +++ b/App/NoOpt/App.lastbuildstate @@ -1,2 +1,2 @@ #v4.0:v110:false -NoOpt|Win32|F:\Trunk2012\BuildWatrbx\| +NoOpt|Win32|J:\Trunk2012\Client\| diff --git a/App/NoOpt/CL.read.1.tlog b/App/NoOpt/CL.read.1.tlog index 3fc5f38..c6a8c94 100644 Binary files a/App/NoOpt/CL.read.1.tlog and b/App/NoOpt/CL.read.1.tlog differ diff --git a/App/NoOpt/CL.write.1.tlog b/App/NoOpt/CL.write.1.tlog index 704a804..6bf9acc 100644 Binary files a/App/NoOpt/CL.write.1.tlog and b/App/NoOpt/CL.write.1.tlog differ diff --git a/App/NoOpt/cl.command.1.tlog b/App/NoOpt/cl.command.1.tlog index 33c98fa..16bc9dc 100644 Binary files a/App/NoOpt/cl.command.1.tlog and b/App/NoOpt/cl.command.1.tlog differ diff --git a/App/gui/ChatOutput.h b/App/gui/ChatOutput.h new file mode 100644 index 0000000..247a2ee --- /dev/null +++ b/App/gui/ChatOutput.h @@ -0,0 +1,199 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Gui/Gui.h" +#include "Util/RunStateOwner.h" +#include "V8DataModel/ChatService.h" +#include "Util/UDim.h" + + +namespace RBX { + + class SafeChat; + class GuiObject; + class BillboardGui; + class ModelInstance; + + namespace Network { + class Player; + class Players; + class ChatMessage; + } + + class ChatLine { + protected: + static float ComputeBubbleLifetime(const std::string& msg, bool isSelf); + + public: + static const char* ELIPSES; + static const int CchMaxChatMessageLength; // max chat message length, including null terminator and elipses. + enum BubbleColor + { + WHITE, + BLUE, + GREEN, + RED, + }; + + enum ChatType + { + PLAYER_CHAT, + PLAYER_TEAM_CHAT, + PLAYER_WHISPER_CHAT, + + GAME_MESSAGE, + PLAYER_GAME_CHAT, + BOT_CHAT, + }; + + std::string message; + const BubbleColor bubbleColor; + const ChatType chatType; + float startTime; + float bubbleDieDelay; + bool isLocalPlayer; + boost::weak_ptr origin; + + const Instance* getOrigin() const { return origin.lock().get(); } + + ChatLine(ChatType chatType, const std::string& message, float startTime, BubbleColor bubbleColor, bool isLocalPlayer); + virtual ~ChatLine() {} + + bool isPlayerChat() const + { + switch(chatType) + { + case PLAYER_CHAT: + case PLAYER_TEAM_CHAT: + case PLAYER_WHISPER_CHAT: + return true; + default: + return false; + } + } + }; + + class PlayerChatLine : public ChatLine { + public: + + static const char* ROBLOXNAME; + Color3 userColor; + + std::string user; + float historyDieDelay; + + const ModelInstance* getCharacter() const; + + PlayerChatLine(ChatType chatType, boost::shared_ptr player, const std::string& message, float startTime, bool isLocalPlayer); + }; + class GameChatLine: public ChatLine + { + public: + GameChatLine(boost::shared_ptr origin, const std::string& message, float startTime, bool isLocalPlayer, BubbleColor bubbleColor); + }; + + struct CharacterChats + { + CharacterChats() + : isVisible(false) + , isMoving(false) + {} + std::deque > fifo; + bool isVisible; + bool isMoving; + weak_ptr billboardGui; + }; + + class ChatOutput + : public GuiItem + { + private: + static const int kMaxTextSize = 16; + static const int kMaxCharsInLine = 20; + + void createBillboardGuiHelper(Instance* instance, bool character); + + void renderBubbleImposters(Adorn* adorn, weak_ptr owner, weak_ptr head); + void renderBubbles(Adorn* adorn, weak_ptr owner, weak_ptr head, bool playerAndGameChat, + Vector3 extentsOffset, Vector3 studsOffset); + typedef GuiItem Super; + static const int MaxChatBubblesPerPlayer; + static const int MaxChatLinesPerBubble; + + RBX::Network::Players* players; + std::map > chatBubble; + std::map > chatBubbleWithTail; + std::map > scalingChatBubbleWithTail; + + struct ScalingInfo + { + Vector2 scalingCutoff; + UDim2 fixedPosition; + UDim2 fixedSize; + + UDim2 scalingPosition; + UDim2 scalingSize; + + UDim2 getPosition(Vector2 size) + { + + return UDim2(size.x < scalingCutoff.x ? scalingPosition.x : fixedPosition.x, + size.y < scalingCutoff.y ? scalingPosition.y : fixedPosition.y); + } + UDim2 getSize(Vector2 size) + { + return UDim2(size.x < scalingCutoff.x ? scalingSize.x : fixedSize.x, + size.y < scalingCutoff.y ? scalingSize.y : fixedSize.y); + } + + ScalingInfo(Vector2 scalingCutoff, UDim2 fixedPosition, UDim2 fixedSize, UDim2 scalingPosition, UDim2 scalingSize) + :scalingCutoff(scalingCutoff) + ,fixedPosition(fixedPosition) + ,fixedSize(fixedSize) + ,scalingPosition(scalingPosition) + ,scalingSize(scalingSize) + {} + ScalingInfo() + {} + }; + std::map scalingInfo; + + std::map > chatPlaceholder; + + std::deque > fifo; + typedef std::map CharacterChatMap; + CharacterChatMap characterSortedMsg; + float time; + + void acceleratedBubbleDecay(ChatLine* line, float wallStep, bool isMoving, bool isVisible); + + void removeOldest(); + bool removeExpired(); + + bool bubbleChatEnabled(); + + std::string SanitizeChatLine(const std::string& msg); // truncate and make safe. + + // Instance + /*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); + + // Listener + rbx::signals::scoped_connection heartbeatConnection; + rbx::signals::scoped_connection playerChatMessageConnection; + rbx::signals::scoped_connection gameChatMessageConnection; + void onHeartbeat(const Heartbeat& heartbeat); + + void onPlayerChatMessage(const Network::ChatMessage& event); + void onGameChatMessage(boost::shared_ptr origin, const std::string& message, ChatService::ChatColor color); + + // GuiItem + /*override*/ void render2d(Adorn* adorn); + /*override*/ void render2d_bubbleStyle(Adorn* adorn, bool playerBubbleChat); + + public: + ChatOutput(); + ~ChatOutput(); + }; + +} // namespace \ No newline at end of file diff --git a/App/gui/ChatWidget.h b/App/gui/ChatWidget.h new file mode 100644 index 0000000..5a4cf8a --- /dev/null +++ b/App/gui/ChatWidget.h @@ -0,0 +1,59 @@ + /* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Gui/GuiDraw.h" + +#include "GfxBase/Adorn.h" + +namespace RBX { + + class UnifiedImageWidget : public UnifiedWidget + { + protected: + GuiDrawImage guiImageDraw; + std::string imageName; + unsigned imageState; + public: + UnifiedImageWidget(const std::string& imageName, int imageState) + : imageName(imageName) + , imageState(imageState) + { + } + + Gui::WidgetState getWidgetState() const; + + /*override*/ void render2dMe(Adorn* adorn); + /*override*/ void setSize(const Vector2& _size) {guiImageDraw.setImageSize(_size);} + /*override*/ Vector2 getSize(Canvas canvas) const {return guiImageDraw.getImageSize();} + }; + + class ChatButton : public UnifiedImageWidget + { + private: + /*override*/ bool isVisible() const; + public: + ChatButton(const std::string& imageName, unsigned imageState) + : UnifiedImageWidget(imageName, imageState) + { + } + }; + + class ChatWidget : public UnifiedWidget + { + private: + typedef UnifiedWidget Super; + std::string findMenuString(GuiItem* item); + + std::string code; + + // Unified Widget + /*override*/ void onMenuStateChanged(); + + /*override*/ GuiResponse process(const shared_ptr& event); + + public: + ChatWidget(const std::string& text, std::string code); + }; + +} // namespace diff --git a/App/gui/EquationDisplay.h b/App/gui/EquationDisplay.h new file mode 100644 index 0000000..632a535 --- /dev/null +++ b/App/gui/EquationDisplay.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Gui/GUI.h" + +namespace RBX { + + class EquationDisplay : public TextDisplay { + private: + std::string equation; + + protected: + // TextDisplay + std::string getLabel() const; + + public: + EquationDisplay( + const std::string& title, + const std::string& equation); + + EquationDisplay( + const std::string& title, + const std::string& label, + const std::string& equation); + + /*override*/ void render2d(Adorn* adorn); + }; +} // namespace \ No newline at end of file diff --git a/App/gui/GUI.h b/App/gui/GUI.h new file mode 100644 index 0000000..b36c11c --- /dev/null +++ b/App/gui/GUI.h @@ -0,0 +1,268 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/InputObject.h" +#include "Gui/GuiEvent.h" +#include "Gui/Layout.h" +#include "V8Tree/Instance.h" +#include "GfxBase/Type.h" +#include "GfxBase/Adorn.h" + +namespace RBX { + + +extern const char* const sGuiItem; +class GuiItem : public DescribedNonCreatable +{ +private: + typedef Instance Super; + shared_ptr focus; // TODO: It should be safe to make this a bald pointer, since focus is always a child + Vector2 guiSize; + + GuiResponse processNonFocus(const shared_ptr& event); + void switchFocus(GuiItem* item); + + // Instance + /*override*/ void onDescendantRemoving(const shared_ptr& instance); + /*override*/ bool askAddChild(const Instance* instance) const { + return Instance::fastDynamicCast(instance)!=0; + } + /*override*/ const RBX::Name& getClassName() const {return RBX::Name::getNullName(); } + +protected: + GuiItem* getFocus() {return focus.get();} + void loseFocus() { + if (focus) { + focus->onLoseFocus(); + } + focus.reset(); + } + + Rect getMyRect(Canvas canvas) const; // myPosition, myPosition + mySize + + Rect2D getMyRect2D(Canvas canvas) const { + return getMyRect(canvas).toRect2D(); + } + + void label2d( + Adorn* adorn, + const std::string& label, + const Color4& fill, + const Color4& border, + Text::XAlign align = Text::XALIGN_LEFT) const; + + ///////////////////////////////////////////////////////////// + // + // GUI Item virtuals + + virtual void onLoseFocus() {} + virtual bool canLoseFocus() {return false;} // in general, Gui Items don't lose focus while processing + + // standard virtual overrides.... + virtual Vector2 getPosition(Canvas canvas) const { + RBXASSERT(getGuiParent()); + return getGuiParent()->getChildPosition(this, canvas); + } + + virtual Vector2 getChildPosition(const GuiItem* child, Canvas canvas) const { // This should always be override if ever used + RBXASSERT(0); + return Vector2::zero(); + } + + // used internally - could these be protected? + virtual int getFontSize() const {return 12;} + + virtual bool isVisible() const {return true;} + + virtual std::string getTitle() {return getName();} + +public: + virtual Vector2 getSize(Canvas canvas) const {return getGuiSize();} + + virtual GuiResponse process(const shared_ptr& event); + + virtual void render2d(Adorn* adorn) {} + + + GuiItem(); + ~GuiItem(); + + void addGuiItem(shared_ptr guiItem) {guiItem->setParent(this);} + + void setGuiSize(const Vector2& size) {guiSize = size;} + const Vector2& getGuiSize() const {return guiSize;} + + GuiItem* getGuiParent(); + const GuiItem* getGuiParent() const; + GuiItem* getGuiItem(int index); + const GuiItem* getGuiItem(int index) const; + + static const Color4& disabledFill(); + static const Color4& translucentBackdrop();// background of the palette + static const Color4& menuSelect(); +}; + +extern const char* const sGuiRoot; + +class GuiRoot : + public Reflection::Described +{ +public: + GuiRoot(); + + /*override*/ void render2d(Adorn* adorn); + + void render2dItem(Adorn* adorn, GuiItem* guiItem); + + // GuiItem + /*override*/ Vector2 getSize(Canvas canvas) const { + // shouldn't be called - implies item doesn't know it's top level + RBXASSERT(false); + return canvas.size; + } + +protected: + virtual bool askSetParent(const Instance* instance) const { + return true; + } +}; + + +class TopMenuBar : public GuiItem { +private: + void init(); + +protected: + Color4 backdropColor; + Layout::Style layoutStyle; + bool visible; + + /*override*/ Vector2 getChildPosition(const GuiItem* child, Canvas canvas) const; + +public: + TopMenuBar() {init();} + TopMenuBar( + const std::string& _title, + Layout::Style layoutStyle, + bool translucentBackdrop = false); + TopMenuBar( + const std::string& _title, + Layout::Style _layoutStyle, + Color4 _backdropColor); + + /*override*/ GuiResponse process(const shared_ptr& event); + /*override*/ void render2d(Adorn* adorn); + /*override*/ Vector2 getSize(Canvas canvas) const; + /*override*/ bool isVisible() const {return visible;} + + void setVisible(bool _set) {visible = _set;} +}; + + + +class RelativePanel : public TopMenuBar { +protected: + Rect::Location xLocation; + Rect::Location yLocation; + Vector2int16 offset; + + void init(const Layout& layout); + +public: + RelativePanel() {init(Layout());} + RelativePanel(const Layout& layout) {init(layout);} + + virtual Vector2 getPosition(Canvas canvas) const; +}; + +// This will become the new unified widget - menu, button, all... +// + +class UnifiedWidget : public GuiItem { +public: + enum MenuState {NOTHING, HOVER, SHOWN_APPEARING, SHOWN}; // + focus if child is shown + +private: + typedef GuiItem Super; + MenuState menuState; + + GuiResponse processShown_InTitle(const shared_ptr& event); + GuiResponse processShown_OutOfTitle(const shared_ptr& event); + + GuiResponse processNothing(const shared_ptr& event); + GuiResponse processHover(const shared_ptr& event); + GuiResponse processShown(const shared_ptr& event); + GuiResponse processKey(const shared_ptr& event); + + void render2dChildren(Adorn* adorn); + + /*override*/ Vector2 getChildPosition(const GuiItem* child, Canvas canvas) const; + /*override*/ bool canLoseFocus() {return true;} + /*override*/ void onLoseFocus(); + /*override*/ int getFontSize() const {return 8;} + + void init(); + +protected: + bool showChildren() {return (menuState >= SHOWN_APPEARING);} + + virtual void onMenuStateChanged() {} + virtual Vector2 firstChildPosition(Canvas canvas) const; + virtual Vector2 childOffset() const; + virtual void render2dMe(Adorn* adorn); + +public: + UnifiedWidget() {init();} + UnifiedWidget(const std::string& title); + + MenuState getMenuState() const {return menuState;} + void setMenuState(MenuState value); + + /*override*/ GuiResponse process(const shared_ptr& event); + /*override*/ void render2d(Adorn* adorn); +}; + + + + + +class TextDisplay : public GuiItem { +private: + typedef GuiItem Super; + + void init(); + +protected: + std::string label; + int fontSize; + Color4 fontColor; + Color4 borderColor; + Text::XAlign align; + bool visible; + + /*override*/ int getFontSize() const {return fontSize;} + /*override*/ bool isVisible() const {return visible;} + + const std::string& getLabel() const {return label;} + +public: + TextDisplay() {init();} + TextDisplay(const std::string& title, const std::string& _label); + + /*override*/ void render2d(Adorn* adorn); + /*override*/ Vector2 getSize(Canvas canvas) const; + + void setLabel(const std::string& _label) {label = _label;} + void setFontSize(int _fontSize) { + fontSize = _fontSize; + setGuiSize(Vector2(fontSize*10.0f, fontSize*2.0f)); + } + void setFontColor(const Color4& _color) {fontColor = _color;} + void setBorderColor(const Color4& _color) {borderColor = _color;} + void setAlign(Text::XAlign _align) {align = _align;} + void setVisible(bool _visible) {visible = _visible;} +}; + + +} // namespace diff --git a/App/gui/GuiDraw.h b/App/gui/GuiDraw.h new file mode 100644 index 0000000..37c2820 --- /dev/null +++ b/App/gui/GuiDraw.h @@ -0,0 +1,76 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/GuiCore.h" +#include "V8Xml/Reference.h" +#include "Util/TextureId.h" +#include "GfxBase/TextureProxyBase.h" +#include "GfxBase/Adorn.h" +#include "rbx/signal.h" + +namespace RBX { + + class Adorn; + + class GuiDrawImage + { + public: + enum ImageState + { + NORMAL = 0x1, + HOVER = 0x2, + DOWN = 0x4, + DISABLE = 0x8, + SELECTED = 0x10, + SELECTED_HOVER = 0x20, + SELECTED_DOWN = 0x40, + ALL = 0x7F + }; + private: + TextureId currentTexture; + TextureId loadingTexture; + RBX::TextureProxyBaseRef disable; + RBX::TextureProxyBaseRef normal; + RBX::TextureProxyBaseRef hover; + RBX::TextureProxyBaseRef down; + RBX::TextureProxyBaseRef selected; + RBX::TextureProxyBaseRef selectedHover; + RBX::TextureProxyBaseRef selectedDown; + mutable Vector2 size; + rbx::signals::scoped_connection unbindResourceSignalHint; + + void OnUnbindResourceSignalHint(); + + void draw(Adorn* adorn, const RBX::TextureProxyBaseRef& texture, const Rect& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, + const Rect& clipRect, const Color4& behind, const Color4& inFront); + + void draw(Adorn* adorn, const RBX::TextureProxyBaseRef& texture, const Rect& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, + const Rotation2D& rotation, const Color4& behind, const Color4& inFront); + + template + void render2dImpl(Adorn* adorn, bool enabled, const Rect& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, + const Modifier& modifier, Gui::WidgetState state, bool isSelected); + + void tryCreateTextureProxy(Adorn *adorn, const std::string& contentString, const std::string& context, RBX::TextureProxyBaseRef& textureRef, bool& isWaiting); + public: + GuiDrawImage() : size(Vector2(0,0)) {} + GuiDrawImage(Adorn *adorn, const std::string& textureName, unsigned imageState) {setImageFromName(adorn, textureName, imageState);} + + void render2d(Adorn* adorn, bool enabled, const Rect& rect, + Gui::WidgetState state, bool isSelected); + void render2d(Adorn* adorn, bool enabled, const Rect& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Rotation2D& rotation, + Gui::WidgetState state, bool isSelected); + void render2d(Adorn* adorn, bool enabled, const Rect& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Rect& clipRect, + Gui::WidgetState state, bool isSelected); + + void setImageSize(const Vector2& _size); + Vector2 getImageSize() const; + + bool setImage(Adorn* adorn, const TextureId& textureId, unsigned imageState, Vector2* outSize = NULL, Instance* contextInstance = NULL, const char* context = ""); + bool setImageFromName(Adorn* adorn, const std::string& textureName, unsigned imageState, Instance* contextInstance = NULL, const char* context = ""); + + void computeUV(Vector2& uvtl, Vector2& uvbr, const Vector2& imageRectOffset, const Vector2& imageRectSize, const Vector2& imageSize); + }; + +} // namespace diff --git a/App/gui/GuiEvent.h b/App/gui/GuiEvent.h new file mode 100644 index 0000000..9c90098 --- /dev/null +++ b/App/gui/GuiEvent.h @@ -0,0 +1,62 @@ +/* Copyright 2003-2015 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "v8tree/Instance.h" + +LOGGROUP(GuiTargetLifetime) + +namespace RBX { + + class GuiResponse + { + private: + enum ResponseType {NOT_SUNK, SUNK}; + enum FinishedType {NOT_FINISHED, FINISHED}; + enum MouseOverType {NOT_MOUSE_OVER, MOUSE_OVER}; + + ResponseType response; + FinishedType finished; + MouseOverType mouseWasOver; + weak_ptr target; + + GuiResponse(ResponseType response, FinishedType finished, MouseOverType mouseWasOver, Instance* newTarget) + : response(response) + , finished(finished) + , mouseWasOver(mouseWasOver) + , target(weak_from(newTarget)) + {} + + GuiResponse(ResponseType response) + : response(response) + , finished(NOT_FINISHED) + , mouseWasOver(NOT_MOUSE_OVER) + {} + + public: + GuiResponse() + : response(NOT_SUNK) + , finished(NOT_FINISHED) + , mouseWasOver(NOT_MOUSE_OVER) + {} + + bool getMouseWasOver() const {return mouseWasOver == MOUSE_OVER;} + void setMouseWasOver() {mouseWasOver = MOUSE_OVER;} + + bool wasSunk() {return (response == SUNK);} + bool wasSunkAndFinished() + { + RBXASSERT(!((finished == FINISHED) && !wasSunk())); + return (finished == FINISHED); + } + + static GuiResponse notSunk() {return GuiResponse(NOT_SUNK);} + static GuiResponse notSunkMouseWasOver() {return GuiResponse(NOT_SUNK, NOT_FINISHED, MOUSE_OVER, NULL);} + static GuiResponse sunk() {return GuiResponse(SUNK);} + static GuiResponse sunkAndFinished() {return GuiResponse(SUNK, FINISHED, NOT_MOUSE_OVER, NULL);} + static GuiResponse sunkWithTarget(Instance* target) {return GuiResponse(SUNK, NOT_FINISHED, NOT_MOUSE_OVER, target);} + + shared_ptr getTarget() { return target.lock(); } + void setTarget(Instance* value) { target = weak_from(value); } + }; +} // namespace diff --git a/App/gui/Layout.h b/App/gui/Layout.h new file mode 100644 index 0000000..5c7e937 --- /dev/null +++ b/App/gui/Layout.h @@ -0,0 +1,29 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/Rect.h" +#include "Util/G3DCore.h" + +namespace RBX { + + class Layout + { + public: + enum Style {HORIZONTAL = 0, VERTICAL = 1}; + + Rect::Location xLocation; + Rect::Location yLocation; + Vector2int16 offset; + Style layoutStyle; + Color4 backdropColor; + + Layout() + : xLocation(Rect::LEFT) + , yLocation(Rect::TOP) + , offset(Vector2int16(0,0)) + , layoutStyle(HORIZONTAL) + , backdropColor(Color4::clear()) + {} + }; +} // namespace diff --git a/App/gui/ProfanityFilter.h b/App/gui/ProfanityFilter.h new file mode 100644 index 0000000..918d5db --- /dev/null +++ b/App/gui/ProfanityFilter.h @@ -0,0 +1,35 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/ScopedSingleton.h" + +namespace RBX { + + class WordList + { + private: + std::set blacklist; + void decrypt(std::string& str); + public: + WordList(); + ~WordList(); + + bool ContainsProfanity(std::string str); + + }; + + + class ProfanityFilter : public ScopedSingleton + { + private: + WordList *wordlist; + bool ContainsProfanityWorker(std::string str); + public: + ProfanityFilter(); + ~ProfanityFilter(); + + static bool ContainsProfanity(const std::string& str); + }; + +} // namespace \ No newline at end of file diff --git a/App/gui/ScoreHud.h b/App/gui/ScoreHud.h new file mode 100644 index 0000000..a8c55ed --- /dev/null +++ b/App/gui/ScoreHud.h @@ -0,0 +1,14 @@ +/* Copyright 2003-2008 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Gui/Gui.h" +#include "Util/RunStateOwner.h" +#include "Network/Player.h" +#include "Network/Players.h" +#include "V8DataModel/team.h" +#include "V8DataModel/teams.h" + +namespace RBX { + +} // namespace \ No newline at end of file diff --git a/App/gui/Widget.h b/App/gui/Widget.h new file mode 100644 index 0000000..e9389b5 --- /dev/null +++ b/App/gui/Widget.h @@ -0,0 +1,47 @@ + /* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Gui/GUI.h" +#include "V8Tree/Verb.h" +#include "V8DataModel/GuiCore.h" +#include "v8datamodel/InputObject.h" + +namespace RBX { + + class Widget : public GuiItem + { + private: + typedef GuiItem Super; + GuiResponse processMouse(const shared_ptr& event); + GuiResponse processKey(const shared_ptr& event); + + protected: + Gui::WidgetState widgetState; + + // This should be standard for all widgets, verb widets, etc. + /*override*/ GuiResponse process(const shared_ptr& event); + /*override*/ void onLoseFocus() { + widgetState = Gui::NOTHING; + Super::onLoseFocus(); + } + + ///////////////////////////////////////////////////////// + // + // Override these to make new widgets + + // GUI ITEM + /*override*/ void render2d(Adorn* adorn); + + // WIDGET + virtual void onClick(const shared_ptr& event) {} + virtual int getFontSize() const {return 10;} + virtual G3D::Color4 getFontColor() {return G3D::Color3::white();} + virtual bool isEnabled() {return isVisible();} + + public: + Widget(); + }; + + +} // namespace \ No newline at end of file diff --git a/App/humanoid/Balancing.h b/App/humanoid/Balancing.h new file mode 100644 index 0000000..dce3f73 --- /dev/null +++ b/App/humanoid/Balancing.h @@ -0,0 +1,39 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/HumanoidState.h" + +namespace RBX { +namespace HUMAN { + + + class Balancing : public HumanoidState + { + private: + float kP; // units: 1/sec^2 torque = kP * momentOfInertia * rotation + float kD; // units: 1/sec torque = kD * momentOfInertia * rotVelocity + + Vector3 lastBalanceTorque; + int tick; + + static int balanceRate(double torqueMag); + static int balanceRateForPGS(); + protected: + static const float maxTorqueComponent() {return 4000.0f;} // units: 1/sec^2 torque <= maxTorqueComponent * momentOfInertia + + void setBalanceP(float P) { kP = P; }; + void setBalanceD(float D) { kD = D; }; + + // Humanoid::State + /*override*/ void onComputeForceImpl(); + + public: + Balancing(Humanoid* humanoid, StateType priorState); + Balancing(Humanoid* humanoid, StateType priorState, const float kP, const float kD); + }; + + +} // namespace HUMAN +} // namespace RBX + diff --git a/App/humanoid/FallingDown.h b/App/humanoid/FallingDown.h new file mode 100644 index 0000000..b8110ea --- /dev/null +++ b/App/humanoid/FallingDown.h @@ -0,0 +1,53 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/HumanoidState.h" + +namespace RBX { + + namespace HUMAN { + + // pure simulation! + + extern const char* const sDead; + class Dead : public Named + { + private: + /*override*/ StateType getStateType() const {return DEAD;} + /*override*/ void onStepImpl(); + /*override*/ void onSimulatorStepImpl(float stepDt); + /*override*/ void onComputeForceImpl() {} + /*override*/ bool enableAutoJump() const { return false; } + public: + Dead(Humanoid* humanoid, StateType priorState); + }; + + + extern const char* const sFallingDown; + class FallingDown : public Named + { + private: + /*override*/ StateType getStateType() const {return FALLING_DWN;} + /*override*/ void onComputeForceImpl() {} + /*override*/ bool enableAutoJump() const { return false; } + public: + FallingDown(Humanoid* humanoid, StateType priorState); + }; + + extern const char* const sPhysics; + class Physics : public Named + { + private: + /*override*/ StateType getStateType() const {return PHYSICS;} + /*override*/ void onComputeForceImpl() {} + /*override*/ bool enableAutoJump() const { return false; } + public: + Physics(Humanoid* humanoid, StateType priorState); + }; + + + + } // namespace HUMAN +} // namespace RBX + diff --git a/App/humanoid/Flying.h b/App/humanoid/Flying.h new file mode 100644 index 0000000..766e7a8 --- /dev/null +++ b/App/humanoid/Flying.h @@ -0,0 +1,32 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/Balancing.h" +#include "Util/Name.h" + +namespace RBX { + namespace HUMAN { + + // Flying occurs when there's no ground below you. You have the ability + // to turn around the y-axis, but not much else. + extern const char* const sFlying; + + class Flying : public Named + { + private: + /*override*/ StateType getStateType() const {return FLYING;} + + protected: + // Humanoid::State + /*override*/ void onSimulatorStepImpl(float stepDt); + /*override*/ void onComputeForceImpl(); + /*override*/ bool enableAutoJump() const { return false; } + + public: + Flying(Humanoid* humanoid, StateType priorState); + }; + + } // namespace HUMAN +} // namespace + diff --git a/App/humanoid/Freefall.h b/App/humanoid/Freefall.h new file mode 100644 index 0000000..a0662f6 --- /dev/null +++ b/App/humanoid/Freefall.h @@ -0,0 +1,51 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/Balancing.h" + +namespace RBX { + + namespace HUMAN { + + extern const char* const sFreefall; + + class Freefall : public Named + { + private: + typedef Named Super; + bool initialized; // hack- some data is bad in the constructor - do first time through; + Vector3 initialLinearVelocity; + Velocity desiredVelocity; // Y is world-up + float torsoFriction; // I set both of these to zero! + float headFriction; + + /*override*/ StateType getStateType() const {return FREE_FALL;} + + /*override*/ void onSimulatorStepImpl(float stepDt); + + /*override*/ void onComputeForceImpl(); + + /*override*/ int ladderCheckRate() { return 0; } + + /*override*/ bool armsShouldCollide() const {return false;} + /*override*/ bool legsShouldCollide() const {return false;} + /*override*/ bool enableAutoJump() const { return false; } + + static float characterVelocityInfluence(); + static float floorVelocityInfluence(); + static float velocityDecay(); + + public: + Freefall(Humanoid* humanoid, StateType priorState); + + ~Freefall(); + + static float kTurnSpeed(); + static float kTurnSpeedForPGS(); + static const float kTurnAccelMax() {return 20000.0f * kTurnSpeed();} // units: 1/sec^2 + }; + + } // namespace HUMAN +} // namespace + diff --git a/App/humanoid/GettingUp.h b/App/humanoid/GettingUp.h new file mode 100644 index 0000000..628c432 --- /dev/null +++ b/App/humanoid/GettingUp.h @@ -0,0 +1,30 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/Humanoid.h" +#include "Humanoid/Balancing.h" +#include "Util/Name.h" + +namespace RBX { + + namespace HUMAN { + + extern const char* const sGettingUp; + + class GettingUp : public Named + { + protected: + /*override*/ StateType getStateType() const {return GETTING_UP;} + + /*override*/ bool armsShouldCollide() const {return false;} + /*override*/ bool legsShouldCollide() const {return false;} + /*override*/ bool enableAutoJump() const { return false; } + + public: + GettingUp(Humanoid* humanoid, StateType priorState); + }; + + } // HUMAN +} // namespace + diff --git a/App/humanoid/Humanoid.h b/App/humanoid/Humanoid.h new file mode 100644 index 0000000..82eeb75 --- /dev/null +++ b/App/humanoid/Humanoid.h @@ -0,0 +1,621 @@ +/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/ICharacterSubject.h" +#include "V8DataModel/IModelModifier.h" +#include "V8DataModel/PartInstance.h" +#include "V8DataModel/Tool.h" +#include "V8World/KernelJoint.h" +#include "V8World/Primitive.h" +#include "Util/SteppedInstance.h" +#include "GfxBase/IAdornable.h" +#include "Util/RunStateOwner.h" +#include "Util/ContentFilter.h" +#include "Util/HeapValue.h" +#include "Humanoid/StatusInstance.h" +#include "Humanoid/HumanoidState.h" + +namespace RBX { + class World; + class RunService; + class Controller; + class Primitive; + class PartInstance; + class ModelInstance; + class JointInstance; + class DataModelMesh; + class Decal; + class Weld; + class Animator; + + namespace HUMAN { + class HumanoidState; + } + + namespace Soundscape { + class SoundChannel; + } + + extern const char* const sHumanoid; + class Humanoid + : public DescribedCreatable + , public KernelJoint // Implements "computeForce" + , public IAdornable + , public ICharacterSubject + , public IModelModifier + , public IStepped + { + public: + enum NameOcclusion + { + NAME_OCCLUSION_NONE = 0, + NAME_OCCLUSION_ENEMY = 1, + NAME_OCCLUSION_ALL = 2, + }; + + enum HumanoidDisplayDistanceType + { + HUMANOID_DISPLAY_DISTANCE_TYPE_VIEWER = 0, + HUMANOID_DISPLAY_DISTANCE_TYPE_SUBJECT = 1, + HUMANOID_DISPLAY_DISTANCE_TYPE_NONE = 2, + }; + + enum HumanoidRigType + { + HUMANOID_RIG_TYPE_R6 = 0, + HUMANOID_RIG_TYPE_R15 = 1, + }; + private: + friend unsigned int HUMAN::HumanoidState::checkComputeEvent(); // only used to check for exploits. + + typedef DescribedCreatable Super; + + ///////////////////////////////////////////// + // REFLECTED DATA + shared_ptr seatPart; // seat the humanoid is sitting in + shared_ptr walkToPart; // if null, then use the walk speed control + Vector3 walkToPoint; // in part coordinate Frame + Vector3 walkDirection; // x == xvalue, y == 0, z== zvalue + Vector3 luaMoveDirection; // unit vector, used to move humanoid continously in that direction + Vector3 rawMovementVector; // unit vector, stores the raw input (never world adjusted) + Vector3 targetPoint; + Vector3 replicatedTargetPoint; + float walkAngleError; + HeapValue walkSpeed; + HeapValue walkSpeedShadow; // due to exploits. + HeapValue percentWalkSpeed; // used to make walk speed variable (for joysticks and the like) + HeapValue health; + HeapValue maxHealth; + mutable ObscureValue walkSpeedErrors; // only used in const member functions... + HeapValue jumpPower; + HeapValue maxSlopeAngle; + HeapValue hipHeight; + bool torsoArrived; + bool jump; + bool autoJump; + bool sit; + bool touchedHard; + bool strafe; + bool localSimulating; // am I simulating this humanoid? + bool ownedByLocalPlayer; // is this my own humanoid? + bool typing; + bool autorotate; + HumanoidRigType rigType; + HeapValue platformStanding; + + bool autoJumpEnabled; + + bool activatePhysics; + Vector3 activatePhysicsImpulse; // + + int ragdollCriteria; + int numContacts; // Used to signal nearlyTouched event + NameOcclusion nameOcclusion; + HumanoidDisplayDistanceType displayDistanceType; + float nameDisplayDistance; + float healthDisplayDistance; + Vector3 cameraOffset; // When this humanoid is used as a camera target, offset the camera target by this vector + + bool isWalkingFromStudioTouchEmulation; + + std::string displayText; + ContentFilter::FilterResult filterState; + + /////////////////////////////////////////////// + // INTERNAL DATA + // Controller interface - server side + bool isWalking; + double walkTimer; + Vector3 getDeltaToGoal() const; + void setWalkMode(bool walking); + void stepWalkMode(double gameDt); + + bool clickToWalkEnabled; + + bool stateTransitionEnabled[HUMAN::NUM_STATE_TYPES]; + + Vector3 lastFloorNormal; + // Humanoid Network Floor Platforms + boost::shared_ptr lastFloorPart; + boost::shared_ptr rootFloorMechPart; + int lastFilterPhase; + + // hack - easiest place to update is on query of had neck + bool hadNeck; // has this humanoid ever had a neck? Only break joints on death if so + bool hadHealth; // has this humanoid ever had health > 0? Only break joints on death if so + + Vector3 pos0; // for looking for movement spikes + Vector3 pos1; + Vector3 pos2; + + void updateHadHealth() { + hadHealth = hadHealth || (health > 0.0f); + } + + typedef enum {TORSO, HEAD, RIGHT_ARM, LEFT_ARM, RIGHT_LEG, LEFT_LEG, VISIBLE_TORSO, APPENDAGE_COUNT} AppendageType; + + shared_ptr appendageCache[APPENDAGE_COUNT]; + shared_ptr baseInstance; + + rbx::signals::scoped_connection characterChildAdded; + rbx::signals::scoped_connection characterChildRemoved; + void onEvent_ChildModified(shared_ptr child); + + boost::unordered_map, rbx::signals::scoped_connection> siblingMap; + void updateSiblingPropertyListener(shared_ptr sibling); + void onEvent_SiblingPropertyChanged(const RBX::Reflection::PropertyDescriptor* desc); + + shared_ptr status; + + rbx::signals::scoped_connection onCFrameChangedConnection; + rbx::signals::scoped_connection humanoidEquipConnection; + + void setCachePointerByType(AppendageType appendage, PartInstance *part); + void updateBaseInstance(); + + const PartInstance* getConstAppendageSlow(AppendageType appendage) const; + PartInstance* getAppendageFast(AppendageType appendage, shared_ptr& appendagePart); + PartInstance* getAppendageSlow(AppendageType appendage); + + World* world; + shared_ptr currentState; + HUMAN::StateType previousState; + + shared_ptr animator; + Animator* getAnimator(); + + void updateLocalSimulating(); // Do I need to simulate this humanoid? + + void onLocalHumanoidEnteringWorkspace(); + + void onCFrameChangedFromReflection(); + + bool hasWalkToPoint(Vector3& worldPosition) const; + + bool canClickToWalk() const; + + void setLocalTransparencyModifier(float transparencyModifier) const; + + void setWalkDirectionInternal(const Vector3& value, bool raiseSignal); + void setJumpInternal(bool value, bool replicate); + + + /////////////////////////////////////////////////////////////////////////// + // Instance + /*override*/ bool askSetParent(const Instance* instance) const; + /*override*/ void onAncestorChanged(const AncestorChanged& event); + /*override*/ void setName(const std::string& value); + /*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); + /*override*/ void onDescendantAdded(Instance* instance); + /*override*/ void onDescendantRemoving(const shared_ptr& instance); + + /////////////////////////////////////////////////////////////////////////// + // IAdornable + /*override*/ bool shouldRender3dAdorn() const {return true;} + /*override*/ bool shouldRender3dSortedAdorn() const {return true;} + /*override*/ Vector3 render3dSortedPosition() const; + /*override*/ void render3dAdorn(Adorn* adorn); + /*override*/ void render3dSortedAdorn(Adorn* adorn); + + void renderMultiplayer(Adorn* adorn, const RBX::Camera& camera); + + void renderBillboard(Adorn* adorn, const RBX::Camera& camera); + void renderBillboardImpl(Adorn* adorn, const Vector2& screenLoc, float fontSize, const Color3& nameTagColor, float nameAlpha, float healthAlpha); + + /////////////////////////////////////////////////////////////////////////// + // SteppedInstance + /*override*/ void onStepped(const Stepped& event); + + /////////////////////////////////////////////////////////////////////////// + // KernelJoint / Connector + /*override*/ void computeForce(bool throttling); + /*override*/ Body* getEngineBody() {return getRootBodyFast();} + /*override*/ virtual KernelType getConnectorKernelType() const { return Connector::HUMANOID; } + + /////////////////////////////////////////////////////////////////////////// + // ILocation + /*override*/ const CoordinateFrame getLocation(); + + /////////////////////////////////////////////////////////////////////////// + // CameraSubject + /*override*/ const CoordinateFrame getRenderLocation(); + /*override*/ const Vector3 getRenderSize(); + + /////////////////////////////////////////////////////////////////////////// + // ICharacterSubject + /*override*/ float getYAxisRotationalVelocity() const; // used for camera control + /*override*/ void setFirstPersonRotationalVelocity(const Vector3& desiredLook, bool firstPersonOn); + /*override*/ void getSelectionIgnorePrimitives(std::vector& primitives); + /*override*/ virtual bool hasFocusCoord() const {return getHeadSlow() != NULL;} + + // Humanoid Platform Networking + bool validateNetworkUpdateDistance(PartInstance* floorPart, CoordinateFrame& previousFloorPosition, float& netDt); + void truncateDisplacementIfObstacle(PartInstance* floorPart, Vector3& newCharPosition, const Vector3& previousCharPosition); + Vector3 getSimulatedFrictionVelocityOffset(PartInstance* floorPart, Vector3& newCharPosition, const Vector3& previousCharPosition, const Vector3& charPosInFloorSpace, const RBX::Velocity& previousFloorVelocity, float& netDt); + const Velocity getHumanoidRelativeVelocityToPart(Primitive* floorPrim); + + public: + void updateNetworkFloorPosition(PartInstance* floorPart, CoordinateFrame& previousFloorPosition, RBX::Velocity& lastFloorVelocity, float& netDt); + bool shouldNotApplyFloorVelocity(Primitive* floorPrim); + bool primitiveIsLastFloor(Primitive* prim); + void updateFloorSimPhaseCharVelocity(Primitive* floorPrim); + // End Humanoid Platform Networking + + + /*override*/ void tellCameraNear(float distance) const; + /*override*/ void tellCameraSubjectDidChange(shared_ptr oldSubject, shared_ptr newSubject) const; + /*override*/ void tellCursorOver(float cursorOffset) const; + /*override*/ void getCameraIgnorePrimitives(std::vector& primitives); + /*override*/ Velocity calcDesiredWalkVelocity() const; // used for camera control + + static float autoTurnSpeed() {return 8.0f;} + + Humanoid(); + ~Humanoid(); + + bool waitingForTorso() { return !torsoArrived; } + bool isLegalForClientToChange(const Reflection::PropertyDescriptor& desc) const; + int getPlayerId(); + + enum Status + { + POISON_STATUS = 0, + CONFUSION_STATUS = 1, + }; + bool hasStatus(Status status); + bool addStatus(Status status); + bool removeStatus(Status status); + bool hasCustomStatus(std::string status); + bool addCustomStatus(std::string status); + bool removeCustomStatus(std::string status); + bool isLocalSimulating() const {return localSimulating;} + bool computeNearlyTouched(); + + bool getStateTransitionEnabled(HUMAN::StateType state); + void setStateTransitionEnabled(HUMAN::StateType state, bool enabled); + + shared_ptr getStatuses(); + rbx::signal statusAddedSignal; + rbx::signal statusRemovedSignal; + rbx::signal customStatusAddedSignal; + rbx::signal customStatusRemovedSignal; + + rbx::remote_signal)> serverEquipToolSignal; + + // Humanoid Network Update Connection + rbx::signals::scoped_connection onPositionUpdatedByNetworkConnection; + + NameOcclusion getNameOcclusion() const { return nameOcclusion; } + void setNameOcclusion(NameOcclusion value); + + HumanoidDisplayDistanceType getDisplayDistanceType() const { return displayDistanceType; } + void setDisplayDistanceType(HumanoidDisplayDistanceType value); + + static Humanoid* humanoidFromBodyPart(Instance* bodyPart); + static const Humanoid* constHumanoidFromBodyPart(const Instance* bodyPart); + static const Humanoid* constHumanoidFromDescendant(const Instance* bodyPart); + + static Humanoid* modelIsCharacter(Instance* testModel); + static const Humanoid* modelIsConstCharacter(const Instance* testModel); + + static Humanoid* getLocalHumanoidFromContext(Instance* context); + static const Humanoid* getConstLocalHumanoidFromContext(const Instance* context); + + static PartInstance* getLocalHeadFromContext(Instance* context); + static const PartInstance* getConstLocalHeadFromContext(const Instance* context); + + static ModelInstance* getCharacterFromHumanoid(Humanoid* humanoid); + static const ModelInstance* getConstCharacterFromHumanoid(const Humanoid* humanoid); + + static PartInstance* getHeadFromCharacter(ModelInstance* character); + static const PartInstance* getConstHeadFromCharacter(const ModelInstance* character); + + static Weld* getGrip(Instance* character); + + static const Vector3 &defaultCharacterCorner() { static Vector3 corner(2.5f,2.5f,2.5f); return corner; } + + // By Definition, these are all called by HumanoidState - these are reflected + rbx::signal diedSignal; + rbx::signal swimmingSignal; + rbx::signal runningSignal; // state change scripts + rbx::signal climbingSignal; // state change scripts + rbx::signal jumpingSignal; // state change scripts + rbx::signal freeFallingSignal; + rbx::signal strafingSignal; + rbx::signal gettingUpSignal; + rbx::signal fallingDownSignal; + rbx::signal ragdollSignal; + rbx::signal)> seatedSignal; + rbx::signal platformStandingSignal; + rbx::signal stateChangedSignal; + rbx::signal stateEnabledChangedSignal; + RBX::HUMAN::StateType getCurrentStateType(); + RBX::HUMAN::StateType getPreviousStateType() { return previousState; }; + void setPreviousStateType(RBX::HUMAN::StateType newState); + void changeState(RBX::HUMAN::StateType state); + static bool isStateInString(const std::string& text, const RBX::HUMAN::StateType &compare, RBX::HUMAN::StateType& value); + + rbx::signal healthChangedSignal; + + // Internal use only - no reflection - happens both client, server + rbx::signal doneSittingSignal; + rbx::signal donePlatformStandingSignal; + + void equipToolInstance(shared_ptr instance); + void equipTool(RBX::Tool* tool); + void unequipTools(); + + void setWalkSpeed(float value); + float getWalkSpeed() const {return walkSpeed;} + void testWalkSpeed(float walkSpeed, float percentWalkSpeed) const + { + walkSpeedErrors = (walkSpeed > walkSpeedShadow) ? walkSpeedErrors + 1 : 0; + if (walkSpeedErrors > 8) + { + RBX::Security::setHackFlagVs(RBX::Security::hackFlag11, HATE_SPEEDHACK); + } + if (fabs(percentWalkSpeed) > 1.01) + { + RBX::Security::setHackFlagVs(RBX::Security::hackFlag11, HATE_SPEEDHACK); + } + } + + void setPercentWalkSpeed(float value); + float getPercentWalkSpeed() const { return percentWalkSpeed; } + + void setJumpPower(float value); + float getJumpPower() const { return jumpPower; } + + void setMaxSlopeAngle(float value); + float getMaxSlopeAngle() const { return maxSlopeAngle; } + + const Vector3 &getLastFloorNormal() const { return lastFloorNormal; } + void setLastFloorNormal(const Vector3 &norm) { lastFloorNormal = norm; } + + void setHipHeight(float value); + float getHipHeight() const { return hipHeight; } + + // Health / damage interface + void setHealth(float value); + void setHealthUi(float value); + void zeroHealthLocal() {health = 0.0f;} // for a non-simulating client -zero out health without bouncing back to the server + float getHealth() const {return health;} + + void setMaxHealth(float value); + float getMaxHealth() const {return maxHealth;} + + void setTyping(bool value) { typing = value; } + bool getTyping() const { return typing; } + + void takeDamage(float value); // honors explosions, etc. + + void setClickToWalkEnabled(bool value) { clickToWalkEnabled = value; } + + // Controller interface - don't set the walk direction directly + void setWalkDirection(const Vector3& value); // compacted - z is in the y place + Vector3 getWalkDirection() const {return walkDirection;} // compacted - z is in the y place + bool allow3dWalkDirection() const; + + void move(Vector3 walkVector, bool relativeToCamera); + + Vector3 getLuaMoveDirection() const { return luaMoveDirection;} + void setLuaMoveDirection(const Vector3& value); + Vector3 getRawMovementVector() const { return rawMovementVector; } + + bool getAutoJumpEnabled() const { return autoJumpEnabled; }; + void setAutoJumpEnabled(bool value); + + void setWalkAngleError(const float &value); + float getWalkAngleError() const {return walkAngleError;} + + void setWalkToPoint(const Vector3& value); + const Vector3& getWalkToPoint() const {return walkToPoint;} + + void setWalkToPart(PartInstance* value); + PartInstance* getWalkToPart() const {return walkToPart.get();} + + void setSeatPart(PartInstance* value); + PartInstance* getSeatPart() const {return seatPart.get();} + + void setJump(bool value); + bool getJump() const { return jump; } + + void setAutoJump(bool value); + bool getAutoJump() const { return autoJump;} + + void setSit(bool value); + bool getSit() const { return sit; } + + void setAutoRotate(bool value); + bool getAutoRotate() const { return autorotate; } + + void setTouchedHard(bool hit) { touchedHard = hit; } + + void setActivatePhysics(bool flag, const Vector3& impulse) + { + activatePhysics = flag; + if (flag) + activatePhysicsImpulse += impulse; + else + activatePhysicsImpulse = impulse; + } + bool getActivatePhysics() { return activatePhysics; } + const Vector3 getActivatePhysicsImpulse() { return activatePhysicsImpulse; } + bool getTouchedHard() { return touchedHard; } + + void setRagdollCriteria(int value); + int getRagdollCriteria() const { return ragdollCriteria; } + + void setPlatformStanding(bool value); + bool getPlatformStanding() const { return platformStanding; } + + void setStrafe(bool value); + bool getStrafe() const { return strafe; } + + bool getDead() const; + + void setHadNeck() {hadNeck = true;} + bool breakJointsOnDeath() const {return hadNeck && hadHealth;} + + void setTargetPoint(const Vector3& value); + void setTargetPointLocal(const Vector3& value); // does not replicate + const Vector3& getTargetPoint() const {return targetPoint;} + + void setNameDisplayDistance(float d); + float getNameDisplayDistance() const { return nameDisplayDistance; } + + void setHealthDisplayDistance(float d); + float getHealthDisplayDistance() const { return healthDisplayDistance; } + + void setCameraOffset(const Vector3 &value); + const Vector3 &getCamearaOffset() const { return cameraOffset; } + + // Humanoid Platform Update Getters and Setters; + void setLastFloor(PartInstance* part) + { + if(lastFloorPart) + { + lastFloorPart.reset(); + } + lastFloorPart = shared_from(part); + } + const shared_ptr& getLastFloor() const { return lastFloorPart; } + + void setRootFloorPart(PartInstance* part) + { + if (rootFloorMechPart) + { + rootFloorMechPart.reset(); + } + rootFloorMechPart = shared_from(part); + } + shared_ptr getRootFloorPart() const { return rootFloorMechPart; } + + int getCurrentFloorFilterPhase(); + int getCurrentFloorFilterPhase(Assembly* floorAssembly); + + void setLastFloorPhase(int phase) { lastFilterPhase = phase; } + int getLastFloorPhase() const { return lastFilterPhase; } + + // Walk Utilities + void moveTo(const Vector3& worldPosition, PartInstance* part); + void moveTo2(Vector3 worldPosition, shared_ptr part); + rbx::signal moveToFinishedSignal; + + bool getUseR15() const { return (rigType != HUMANOID_RIG_TYPE_R6); } + Humanoid::HumanoidRigType getRigType() const { return rigType; } + void setRigType(Humanoid::HumanoidRigType type); + + // Build Joints + void buildJoints(RBX::DataModel* dm = NULL); + void buildJointsFromAttachments(PartInstance* part, std::vector& characterParts); + + JointInstance* getRightShoulder(); + Joint* getNeck(); + + // Primitive + void getPrimitives(std::vector& primitives) const; + + void getParts(std::vector& primitives) const; + + PartInstance* getTorsoDangerous() const; // reflection only + PartInstance* getLeftLegDangerous() const; // reflection only + PartInstance* getRightLegDangerous() const; // reflection only + + // TODO: No internal buffering - after refactor, rename without the word slow + PartInstance* getTorsoSlow(); // no internal buffering + PartInstance* getVisibleTorsoSlow(); + PartInstance* getHeadSlow(); + PartInstance* getLeftLegSlow(); + PartInstance* getRightLegSlow(); + PartInstance* getLeftArmSlow(); + PartInstance* getRightArmSlow(); + StatusInstance* getStatusSlow(); + + const PartInstance* getTorsoSlow() const; // no internal buffering + const PartInstance* getVisibleTorsoSlow() const; + const PartInstance* getHeadSlow() const; + const PartInstance* getLeftLegSlow() const; + const PartInstance* getRightLegSlow() const; + const PartInstance* getLeftArmSlow() const; + const PartInstance* getRightArmSlow() const; + const StatusInstance* getStatusSlow() const; + + Primitive* getTorsoPrimitiveSlow(); + Primitive* getHeadPrimitiveSlow(); + + // Internal buffering + PartInstance* getTorsoFast(); + PartInstance* getVisibleTorsoFast(); + PartInstance* getHeadFast(); + PartInstance* getLeftLegFast(); + PartInstance* getRightLegFast(); + PartInstance* getLeftArmFast(); + PartInstance* getRightArmFast(); + StatusInstance* getStatusFast(); + + Primitive* getTorsoPrimitiveFast(); + + // Body + inline Body* getTorsoBodyFast() + { + Primitive* prim = getTorsoPrimitiveFast(); + return prim ? prim->getBody() : NULL; + } + Body* getRootBodyFast(); + + // Attachment Points + CoordinateFrame getTopOfHead() const; + CoordinateFrame getRightArmGrip() const; + + float getTorsoHeading() const; // 0 == North == -Z. Pi/2 = WEST == -x + float getTorsoElevation() const; + + void setTorso(PartInstance* value); + void setLeftLeg(PartInstance* value); + void setRightLeg(PartInstance* value); + + void setHeadMesh(DataModelMesh* value); + void setHeadDecal(Decal* value); + + World* getWorld() {return world;}; + const World* getConstWorld() const {return world;}; + + static void renderWaypoint(Adorn* adorn, const Vector3& waypoint); + + // proxy interface to Animator object. + shared_ptr loadAnimation(shared_ptr animation); + bool CheckTorso(); + void setupAnimator(); + shared_ptr getPlayingAnimationTracks(); + + rbx::signal)> animationPlayedSignal; + + bool getOwnedByLocalPlayer() const { return ownedByLocalPlayer; } + + bool getWalkingFromStudioTouchEmulation() const { return isWalkingFromStudioTouchEmulation; } + void setWalkingFromStudioTouchEmulation(bool value) { isWalkingFromStudioTouchEmulation = value; } + }; + +} // namespace RBX diff --git a/App/humanoid/HumanoidState.h b/App/humanoid/HumanoidState.h new file mode 100644 index 0000000..e37ff8e --- /dev/null +++ b/App/humanoid/HumanoidState.h @@ -0,0 +1,346 @@ +/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + + +#include "Util/HitTestFilter.h" +#include "GfxBase/IAdornable.h" +#include "Util/Name.h" +#include "rbx/Debug.h" +#include "Util/Velocity.h" +#include "rbx/boost.hpp" +#include "Reflection/Event.h" +#include "G3D/Array.h" +#include "util/PartMaterial.h" + +#include + +#define CHARACTER_FORCE_DEBUG 0 + +namespace RBX +{ + class Humanoid; + class PartInstance; + class Controller; + class Assembly; + class GeometryService; + + namespace HUMAN + { + typedef enum { FALLING_DWN = 0, + RAGDOLL, // 1 + GETTING_UP, + JUMPING, + SWIMMING, + FREE_FALL, // 4: balancing, no thrust + FLYING, + LANDED, // 6: can't jump + RUNNING, // 7 + RUNNING_SLAVE, // 8: slave side - lock to running mode to do accurate physics when touched slave side + RUNNING_NO_PHYS, // 9 + STRAFING_NO_PHYS, + CLIMBING, + SEATED, + PLATFORM_STANDING, + DEAD, + PHYSICS, + NUM_STATE_TYPES, + xx } StateType; // XX == NO change + + typedef enum { NO_HEALTH = 0, // Humanoid Commands + NO_NECK, + JUMP_CMD, + STRAFE_CMD, + NO_STRAFE_CMD, + SIT_CMD, + NO_SIT_CMD, + PLATFORM_STAND_CMD, + NO_PLATFORM_STAND_CMD, + TIPPED, // Tilting + UPRIGHT, + FACE_LDR, // Ladder + AWAY_LDR, + OFF_FLOOR, // Floor + OFF_FLOOR_GRACE, // Floor w/ Grace Period + ON_FLOOR, + TOUCHED, // Other Objects + NEARLY_TOUCHED, // Other Objects + TOUCHED_HARD, + ACTIVATE_PHYSICS, + FINISHED, + TIMER_UP, // FINISHED_FALLING, READY_TO_JUMP, + NO_TOUCH_ONE_SECOND, + HAS_GYRO, + HAS_BUOYANCY, + NO_BUOYANCY, + NUM_EVENT_TYPES } EventType; + +#if CHARACTER_FORCE_DEBUG + class DebugRay + { + public: + RbxRay ray; + Color3 color; + + DebugRay(const RbxRay& _ray, const Color3& _color) + { + ray = _ray; + color = _color; + } + + void Draw(Adorn* adorn); + }; +#endif + + class HumanoidState : public INamed, + public HitTestFilter + + { + public: + static const unsigned int kCorrectCheckValue = 2; + + private: + const Vector3& unitializedFloorTouch() const { + static Vector3 v(1e15f, 1e15f, 1e15f); + return v; + } + + Humanoid* humanoid; + float timer; + float noTouchTimer; + bool nearlyTouched; + bool shouldRender; + bool finished; + bool outOfWater; + bool headClear; + StateType priorState; + StateType luaState; + + G3D::Array foundParts; // temp buffer + bool facingLadder; + shared_ptr floorPart; + PartMaterial floorMaterial; + Vector3 floorTouchInWorld; + Vector3 floorTouchNormal; + Vector3 floorHumanoidLocationInWorld; + float noFloorTimer; + + // Cut down firing of the running event to when there are major difference in velocity + float lastMovementVelocity; + + // signals that the state needs these updated + bool usesEvent(EventType e) const; + bool usesLadder() const {return (usesEvent(FACE_LDR) || usesEvent(AWAY_LDR));} + bool usesFloor() const {return (usesEvent(OFF_FLOOR) || usesEvent(ON_FLOOR) || usesEvent(OFF_FLOOR_GRACE));} + + float computeTilt() const; + bool computeTipped() const; + bool computeUpright() const; + bool computeHasGyro() const; + bool computeJumped() const; + float computeFloorTilt() const; + + void setLegsCanCollide(bool canCollide); + void setArmsCanCollide(bool canCollide); + void setHeadCanCollide(bool canCollide); + void setTorsoCanCollide(bool canCollide); + + int ladderCheck; + virtual int ladderCheckRate() { return 2; } + bool findPrimitiveInLadderZone(Adorn* adorn); + bool findLadder(Adorn* adorn); + void doLadderRaycast(GeometryService *geom, const RbxRay& caster,Humanoid* humanoid, Primitive** hitPrimOut, + Vector3* hitLocationOut); + void doAutoJump(); + void findFloor(shared_ptr& oldFloor); + shared_ptr tryFloor(const RbxRay& ray, Vector3& hitLocation, Vector3& hitNormal, float maxDistance, Assembly* humanoidAssembly, PartMaterial& recentFloorMaterial); + void AverageFloorRayCast(shared_ptr &floorPart, Vector3& floorPartHitLocation, Vector3& floorPartHitNormal, + PartMaterial& floorPartHitMaterial, Vector3& hitLocationAccumulator, int& hitLocationCount, const bool UpdateFloorPart, + const Vector3& offset, const float maxDistance, Assembly* humanoidAssembly, const CoordinateFrame& torsoC); + void preStepFloor(); + void preStepCollide(); + void preStepSimulatorSide(float dt); + void preStepSlaveSide() {preStepCollide();} + + static void doSimulatorStateTable(shared_ptr& state, float dt); + static void doSlaveStateTable(shared_ptr& state, StateType newType); + static HumanoidState* create(StateType newType, StateType oldType, Humanoid* humanoid); + static HumanoidState* createNew(StateType newType, StateType oldType, Humanoid* humanoid); + static void changeState(shared_ptr& state, StateType newType); + + void fireEvent(StateType stateType, bool entering); + + // Override hitTestFiler + /*override*/ Result filterResult(const Primitive* testMe) const; + + protected: + + // For debugging + Vector3 maxTorque; + Vector3 maxForce; + float maxContactVel; + Vector3 lastTorque; + Vector3 lastForce; + float lastContactVel; + + static float minMoveVelocity() {return 0.5f;} + static float maxClimbDistance() {return 2.45f;} // studs + static const Vector3 maxMoveForce() {static Vector3 m(1000.0f, 10000.0f, 1000.0f); return m;} //const Vector3 maxMoveAccelerationGrid(5e4f*0.02f, 5e5f*0.02f, 1e4f*0.02f); + static const Vector3 minMoveForce() {static Vector3 m(-1000.0f, 0.0f, -1000.0f); return m;} + static const Vector3 maxSwimmingMoveForce() {static Vector3 m(10000.0f, 1000.0f, 10000.0f); return m;} + static const Vector3 minSwimmingMoveForce() {static Vector3 m(-10000.0f, -10000.0f, -10000.0f); return m;} + static float fallDelay() { return 0.125f; } + static float maxLinearMoveForce() { return 143.0; } + + // Need public for some Physics calculations outside of Humanoid State + public: + float steepSlopeAngle() const; + static float runningKMoveP(); + static float runningKMovePForPGS(); + static float maxLinearGroundMoveForce() { return 500.0; } + + protected: + Assembly* filteringAssembly; + + bool computeEvent(EventType eventType); + + bool computeTouched(); + bool computeNearlyTouched(); + bool computeTouchedByMySimulation(); + bool computeTouchedHard(); + bool computeActivatePhysics(); + + void setOutOfWater() { outOfWater = true; } + bool getOutOfWater() const { return outOfWater; } + + void setTimer(float time) {timer = time;} + float getTimer() const {return timer;} + + bool getFinished() const {return finished;} + void setFinished(bool value) {finished = value;} + + bool getFacingLadder() const {return facingLadder;} + bool getHeadClear() const { return headClear; } + + PartMaterial getFloorMaterial() const { return floorMaterial; } + float getFloorFrictionProperty(Primitive* floorPrim) const; + + Primitive* getFloorPrimitive(); + const Primitive* getFloorPrimitiveConst() const; + const Vector3& getFloorTouchInWorld() const { + RBXASSERT(floorTouchInWorld != unitializedFloorTouch()); + return floorTouchInWorld; + } + const Vector3& getFloorTouchNormal() const { + RBXASSERT(floorTouchInWorld != unitializedFloorTouch()); + return floorTouchNormal; + } + const Vector3& getFloorHumanoidLocationInWorld() const { + RBXASSERT(floorHumanoidLocationInWorld != unitializedFloorTouch()); + return floorHumanoidLocationInWorld; + } + + + const Velocity getFloorPointVelocity(); + Vector3 getRelativeMovementVelocity(); + float getDesiredAltitude() const; + +#if CHARACTER_FORCE_DEBUG + std::vector debugRayList; +#endif + + void fireMovementSignal(rbx::signal& movementSignal, float movementVelocity); + + // Tick Count - 30 FPS, state table occurs here + virtual void onComputeForceImpl() = 0; + virtual void onStepImpl() {} + virtual void onSimulatorStepImpl(float stepDt) {} + + protected: + void setCanThrottleState(bool canThrottle); // only the Seated state can throttle - its joined to parts so it must + + // Attributes moved in assemblies + Assembly* getAssembly(); + const Assembly* getAssemblyConst() const; + void stateToAssembly(); + StateType stateFromAssembly(); + + public: + virtual bool armsShouldCollide() const {return true;} + virtual bool legsShouldCollide() const {return true;} + virtual bool headShouldCollide() const {return true;} + virtual bool torsoShouldCollide() const {return true;} + + virtual bool enableAutoJump() const { return true; } + + virtual void onCFrameChangedFromReflection() { preStepFloor();} // recalculate floor part + + + HumanoidState(Humanoid* humanoid, StateType priorState); + + virtual ~HumanoidState(); + + const Humanoid* getHumanoidConst() const; + + Humanoid* getHumanoid() { + return const_cast(getHumanoidConst()); + } + + static HumanoidState* defaultState(Humanoid* humanoid); // new Running(this)); + + static void simulate(shared_ptr& state, float dt); + + static void updateHumanoidFloorStatus(shared_ptr& state); + + static bool hasFloorChanged(shared_ptr& state, Primitive* lastFloorPrim); + + static void noSimulate(shared_ptr& state); // for non-simulating humanoids, match states + + virtual void fireEvents(); + + virtual float getYAxisRotationalVelocity() const {return 0.0f;} + + float getCharacterHipHeight() const; + + void onComputeForce(); + + bool torsoHasBuoyancy, leftLegHasBuoyancy, rightLegHasBuoyancy; + std::vector buoyancyConnections; + void setTorsoHasBuoyancy( bool value ) { torsoHasBuoyancy = value; } + void setLeftLegHasBuoyancy( bool value ) { leftLegHasBuoyancy = value; } + void setRightLegHasBuoyancy( bool value ) { rightLegHasBuoyancy = value; } + bool computeHasBuoyancy(); + + virtual StateType getStateType() const = 0; + + void setLuaState(StateType state); + StateType getLuaState() { return luaState; } + + static const char *getStateNameByType(StateType state); + + bool computeHitByHighImpactObject(); + + // only in debug + void render3dAdorn(Adorn* adorn); + + void setNearlyTouched(); + + // for security purposes, get the address of this code. + // A member function pointer is a compiler defined data structure. + static inline const void* getComputeEventBaseAddress() + { +#ifdef _WIN32 + bool (RBX::HUMAN::HumanoidState::* hsce)(EventType) = &computeEvent; + if(sizeof(hsce) == 8 || sizeof(hsce) == 4) + { + return (const void*&)(hsce); // odd, but required syntax for this horrible conversion. + } +#endif + return NULL; + } + + unsigned int checkComputeEvent(); // this was added due to exploits. + }; + + } // namespace HUMAN +} // namespace RBX diff --git a/App/humanoid/Jumping.h b/App/humanoid/Jumping.h new file mode 100644 index 0000000..ab3eef7 --- /dev/null +++ b/App/humanoid/Jumping.h @@ -0,0 +1,43 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/Flying.h" + +namespace RBX { + namespace HUMAN { + + extern const char* const sJumping; + + class Jumping : public Named + { + private: + typedef Named Super; + /*override*/ StateType getStateType() const {return JUMPING;} + + // Humanoid::State + /*override*/ void onComputeForceImpl(); + + /*override*/ bool armsShouldCollide() const {return false;} + /*override*/ bool legsShouldCollide() const {return false;} + /*override*/ bool torsoShouldCollide() const {return false;} + + // Override hitTestFiler + /*override*/ Result filterResult(const Primitive* testMe) const; + + bool findCeiling(); + shared_ptr tryCeiling(const RbxRay& ray, float maxDistance, Assembly* humanoidAssembly); + + Vector3 jumpDir; + + public: + Jumping(Humanoid* humanoid, StateType priorState); + + static float kJumpP() {return 500.0f;} + static float kJumpVelocityGrid() {return 50.0f;} + + }; + + } // namespace HUMAN +} // namespace + diff --git a/App/humanoid/MovingNoPhysicsBase.h b/App/humanoid/MovingNoPhysicsBase.h new file mode 100644 index 0000000..e806d2d --- /dev/null +++ b/App/humanoid/MovingNoPhysicsBase.h @@ -0,0 +1,51 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/HumanoidState.h" +#include "V8World/Mechanism.h" + +namespace RBX { + class Assembly; + class PhysicsService; + +namespace HUMAN { + + extern const char* const sMovingNoPhysicsBase; + + class MovingNoPhysicsBase + : public Named + { + private: + typedef Named Super; + /*override*/ StateType getStateType() const {return RUNNING_NO_PHYS;} + /*override*/ void fireEvents(); + + shared_ptr torsoPart; + weak_ptr physicsService; + + rbx::signals::scoped_connection torsoAncestryChanged; + void onEvent_TorsoAncestryChanged(); + void disconnectTorso(); + + const Assembly* getAssemblyConst() const; + + void applyImpulseToFloor(float dt); + + protected: + + // Humanoid::State + /*override*/ void onSimulatorStepImpl(float stepDt); + /*override*/ void onComputeForceImpl(); + + /*override*/ bool armsShouldCollide() const {return false;} + /*override*/ bool legsShouldCollide() const {return false;} + /*override*/ bool headTorsoShouldCollide() const {return false;} + + public: + MovingNoPhysicsBase(Humanoid* humanoid, StateType priorState); + ~MovingNoPhysicsBase(); + }; + + } // namespace HUMAN +} // namespace diff --git a/App/humanoid/Ragdoll.h b/App/humanoid/Ragdoll.h new file mode 100644 index 0000000..51f04a4 --- /dev/null +++ b/App/humanoid/Ragdoll.h @@ -0,0 +1,30 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/Balancing.h" +#include "Util/Name.h" + +namespace RBX { + namespace HUMAN { + + // Flying occurs when there's no ground below you. You have the ability + // to turn around the y-axis, but not much else. + extern const char* const sRagdoll; + + class Ragdoll : public Named + { + private: + typedef Named Super; + /*override*/ StateType getStateType() const {return RAGDOLL;} + /*override*/ void onStepImpl(); + /*override*/ void onComputeForceImpl() {} + /*override*/ bool enableAutoJump() const { return false; } + public: + Ragdoll(Humanoid* humanoid, StateType priorState); + ~Ragdoll(); + }; + + } // namespace HUMAN +} // namespace + diff --git a/App/humanoid/Running.h b/App/humanoid/Running.h new file mode 100644 index 0000000..193e090 --- /dev/null +++ b/App/humanoid/Running.h @@ -0,0 +1,66 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/RunningBase.h" + +namespace RBX { + + class Body; + + namespace HUMAN { + + extern const char* const sRunning; + extern const char* const sRunningSlave; + extern const char* const sLanded; + extern const char* const sClimbing; + + + class Running : public Named + { + private: + typedef Named Super; + /*override*/ StateType getStateType() const {return RUNNING;} + /*override*/ void fireEvents(); + + protected: + /*override*/ void onComputeForceImpl(); + + public: + Running(Humanoid* humanoid, StateType priorState); + }; + + // Slave side only - stays running until some other change to respond to touch events correctly + class RunningSlave : public Named + { + public: + RunningSlave(Humanoid* humanoid, StateType priorState); + }; + + class Landed : public Named + { + private: + /*override*/ StateType getStateType() const {return LANDED;} + + public: + Landed(Humanoid* humanoid, StateType priorState); + }; + + + class Climbing : public Named + { + private: + typedef Named Super; + /*override*/ StateType getStateType() const {return CLIMBING;} + /*override*/ void fireEvents(); + /*override*/ int ladderCheckRate() { return 0; } + /*override*/ bool enableAutoJump() const { return false; } + public: + Climbing(Humanoid* humanoid, StateType priorState) : Named(humanoid, priorState) + {} + + }; + + } // namespace HUMAN +} // namespace RBX + diff --git a/App/humanoid/RunningBase.h b/App/humanoid/RunningBase.h new file mode 100644 index 0000000..5c50000 --- /dev/null +++ b/App/humanoid/RunningBase.h @@ -0,0 +1,51 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/Balancing.h" + + +namespace RBX { + class Body; + +namespace HUMAN { + + class RunningBase : public Balancing + { + private: + typedef Balancing Super; + protected: + Velocity floorVelocity; + + // The following velocities are w.r.t the ground that the figure is walking on. The ground may be moving + Velocity desiredVelocity; // desired velocity in world coordinates relative to the floor's velocity + float desiredAltitude; // ignored if 0.0 + + void rotateWithGround(Body* body); + void hoverOnFloor(Body* body); + void move(Body* body); + + + /////////////////////////////////////////////////////////////////////////// + // Humanoid::State + /*override*/ void onComputeForceImpl(); + /*override*/ void onSimulatorStepImpl(float stepDt); + + /*override*/ float getYAxisRotationalVelocity() const {return desiredVelocity.rotational.y;} + + /*override*/ bool armsShouldCollide() const {return false;} + /*override*/ bool legsShouldCollide() const {return false;} + + public: + RunningBase(Humanoid* humanoid, StateType priorState); + RunningBase(Humanoid* humanoid, StateType priorState, const float kP, const float kD); + + /*override*/ void onCFrameChangedFromReflection(); + + static const float kTurnP() {return 7500.0f;} + static const float kTurnPForRotatePGS() {return 450.0f;} + static const float kTurnPForFreeFallPGS() {return 375.0f;} + }; + + } // namespace HUMAN +} // namespace RBX \ No newline at end of file diff --git a/App/humanoid/RunningNoPhysics.h b/App/humanoid/RunningNoPhysics.h new file mode 100644 index 0000000..2ce0320 --- /dev/null +++ b/App/humanoid/RunningNoPhysics.h @@ -0,0 +1,24 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/MovingNoPhysicsBase.h" + +namespace RBX { + class Clump; + +namespace HUMAN { + + extern const char* const sRunningNoPhysics; + + class RunningNoPhysics + : public Named + { + private: + /*override*/ StateType getStateType() const {return RUNNING_NO_PHYS;} + public: + RunningNoPhysics(Humanoid* humanoid, StateType priorState); + }; + + } // namespace HUMAN +} // namespace diff --git a/App/humanoid/Seated.h b/App/humanoid/Seated.h new file mode 100644 index 0000000..814e1d0 --- /dev/null +++ b/App/humanoid/Seated.h @@ -0,0 +1,48 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/HumanoidState.h" + +namespace RBX { + + namespace HUMAN { + + extern const char *const sSeated; + + class Seated : public Named + { + private: + /*override*/ StateType getStateType() const {return SEATED;} + + /*override*/ bool armsShouldCollide() const {return false;} + /*override*/ bool legsShouldCollide() const {return false;} + /*override*/ void onComputeForceImpl() {} + /*override*/ bool enableAutoJump() const { return false; } + + public: + Seated(Humanoid* humanoid, StateType priorState); + ~Seated(); + }; + + + extern const char* const sPlatformStanding; + + class PlatformStanding : public Named + { + private: + /*override*/ StateType getStateType() const {return PLATFORM_STANDING;} + + /*override*/ bool armsShouldCollide() const {return false;} + /*override*/ bool legsShouldCollide() const {return false;} + /*override*/ void onComputeForceImpl() {} + /*override*/ bool enableAutoJump() const { return false; } + + public: + PlatformStanding(Humanoid* humanoid, StateType priorState); + ~PlatformStanding(); + }; + + } // namespace +} // namespace + diff --git a/App/humanoid/StatusInstance.h b/App/humanoid/StatusInstance.h new file mode 100644 index 0000000..8bf313e --- /dev/null +++ b/App/humanoid/StatusInstance.h @@ -0,0 +1,24 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/ModelInstance.h" + +namespace RBX { + + extern const char* const sStatusInstance; + class StatusInstance + : public DescribedCreatable + { + private: + typedef DescribedCreatable Super; + + public: + StatusInstance(); + + protected: + /*override*/ bool askSetParent(const Instance* instance) const; + /*override*/ bool askForbidParent(const Instance* instance) const { return !askSetParent(instance); } + }; + +} // namespace diff --git a/App/humanoid/StrafingNoPhysics.h b/App/humanoid/StrafingNoPhysics.h new file mode 100644 index 0000000..8542f9e --- /dev/null +++ b/App/humanoid/StrafingNoPhysics.h @@ -0,0 +1,24 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/MovingNoPhysicsBase.h" + +namespace RBX { + class Clump; + +namespace HUMAN { + + extern const char* const sStrafingNoPhysics; + + class StrafingNoPhysics + : public Named + { + private: + /*override*/ StateType getStateType() const {return STRAFING_NO_PHYS;} + public: + StrafingNoPhysics(Humanoid* humanoid, StateType priorState); + }; + + } // namespace HUMAN +} // namespace diff --git a/App/humanoid/Swimming.h b/App/humanoid/Swimming.h new file mode 100644 index 0000000..c57be7f --- /dev/null +++ b/App/humanoid/Swimming.h @@ -0,0 +1,43 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Humanoid/Balancing.h" +#include "Util/Name.h" + +namespace RBX { + namespace HUMAN { + + extern const char* const sSwimming; + + class Swimming : public Named + { + private: + typedef Named Super; + Vector3 initialLinearVelocity; + static float velocityDecay(); + + /*override*/ StateType getStateType() const {return SWIMMING;} + /*override*/ void fireEvents(); + /*override*/ bool enableAutoJump() const { return false; } + + Velocity desiredVelocity; + + protected: + /////////////////////////////////////////////////////////////////////////// + // Humanoid::State + /*override*/ void onComputeForceImpl(); + /*override*/ void onSimulatorStepImpl(float stepDt); + + public: + Swimming(Humanoid* humanoid, StateType priorState); + + + + static const float kTurnSpeed() {return 6.0f;} // note Humanoid autoTurnSpeed is 8.0f; + static const float kTurnAccelMax() {return 20000.0f * kTurnSpeed();} + }; + + } // namespace HUMAN +} // namespace + diff --git a/App/include/reflection/Type.h b/App/include/reflection/Type.h index c1cee53..0fede78 100644 --- a/App/include/reflection/Type.h +++ b/App/include/reflection/Type.h @@ -58,7 +58,7 @@ namespace RBX :Descriptor(name, Descriptor::Attributes()) ,tag(Name::lookup(name)) ,isNumber(boost::is_arithmetic::value) - ,isFloat(boost::is_floating_point::value) + ,isFloat(boost::is_float::value) ,isEnum(false) { *isOutdated = false; @@ -71,7 +71,7 @@ namespace RBX :Descriptor(name, Descriptor::Attributes()) ,tag(Name::declare(tag)) ,isNumber(boost::is_arithmetic::value) - ,isFloat(boost::is_floating_point::value) + ,isFloat(boost::is_float::value) ,isEnum(false) { RBXASSERT(!this->tag.empty()); diff --git a/App/include/reflection/reflection.h b/App/include/reflection/reflection.h index c2b8a72..4f859da 100644 --- a/App/include/reflection/reflection.h +++ b/App/include/reflection/reflection.h @@ -133,7 +133,7 @@ namespace RBX /*implement*/ void setValue(DescribedBase* object, const V& value) const { Class* c = boost::polymorphic_downcast(object); - set(c, value); + (c->*set)(value); } }; diff --git a/App/include/v8datamodel/DataModel.h b/App/include/v8datamodel/DataModel.h index cb23da7..c855cea 100644 --- a/App/include/v8datamodel/DataModel.h +++ b/App/include/v8datamodel/DataModel.h @@ -125,7 +125,6 @@ public: rbx::signal screenshotReadySignal; rbx::signal screenshotUploadSignal; - rbx::signal graphicsQualityShortcutSignal; rbx::signal allowedGearTypeChanged; @@ -227,8 +226,6 @@ private: bool networkStatsWindowsOn; - bool isRobloxApp; - bool renderGuisActive; RBX::Game* game; @@ -251,8 +248,6 @@ private: public: static bool BlockingDataModelShutdown; - static bool isXboxApp; - static unsigned int perfStats; // another bitmask used to record detected hacks std::string jobId; @@ -384,9 +379,6 @@ public: bool isStudio() const { return runningInStudio; } void setIsStudio(bool runningInStudio); - bool getIsXboxApp() const { return isXboxApp; } - void setIsXboxApp(bool value); - bool isRunMode() const { return isStudioRunMode; } void setIsRunMode(bool value); @@ -499,8 +491,6 @@ public: void save(ContentId contentId); static bool canSave(const RBX::Instance* instance); - - bool getRemoteBuildMode(); void setRemoteBuildMode(bool remoteBuildMode); diff --git a/App/include/v8datamodel/GAMEBASICSETTINGS.H b/App/include/v8datamodel/GAMEBASICSETTINGS.H index 00e8530..81becc5 100644 --- a/App/include/v8datamodel/GAMEBASICSETTINGS.H +++ b/App/include/v8datamodel/GAMEBASICSETTINGS.H @@ -5,33 +5,37 @@ namespace RBX { - extern const char *const sGameBasicSettings; + extern const char* const sGameBasicSettings; class GameBasicSettings : public GlobalBasicSettingsItem { typedef GlobalBasicSettingsItem Super; public: - enum ControlMode {CONTROL_CLASSIC = 0, CONTROL_MOUSELOCK = 1, CONTROL_HYBRID = 2, CONTROL_CAMLOCK = 3, CONTROL_MOUSEPAN = 4}; - enum RenderQualitySetting {QUALITY_AUTO = 0, QUALITY_1 = 1, QUALITY_2 = 2, QUALITY_3 = 3, QUALITY_4 = 4, QUALITY_5 = 5, QUALITY_6 = 6, QUALITY_7 = 7, QUALITY_8 = 8, QUALITY_9 = 9, QUALITY_10 = 10}; - enum CameraMode {CAMERA_MODE_DEFAULT = 0, CAMERA_MODE_CLASSIC = 1, CAMERA_MODE_FOLLOW = 2}; + enum ControlMode { CONTROL_CLASSIC = 0, CONTROL_MOUSELOCK = 1, CONTROL_HYBRID = 2, CONTROL_CAMLOCK = 3, CONTROL_MOUSEPAN = 4 }; + enum RenderQualitySetting { QUALITY_AUTO = 0, QUALITY_1 = 1, QUALITY_2 = 2, QUALITY_3 = 3, QUALITY_4 = 4, QUALITY_5 = 5, QUALITY_6 = 6, QUALITY_7 = 7, QUALITY_8 = 8, QUALITY_9 = 9, QUALITY_10 = 10 }; + enum CameraMode { CAMERA_MODE_DEFAULT = 0, CAMERA_MODE_CLASSIC = 1, CAMERA_MODE_FOLLOW = 2 }; enum TouchCameraMovementMode { - TOUCH_CAMERA_MOVEMENT_MODE_DEFAULT = 0, - TOUCH_CAMERA_MOVEMENT_MODE_CLASSIC = 1, - TOUCH_CAMERA_MOVEMENT_MODE_FOLLOW = 2 }; + TOUCH_CAMERA_MOVEMENT_MODE_DEFAULT = 0, + TOUCH_CAMERA_MOVEMENT_MODE_CLASSIC = 1, + TOUCH_CAMERA_MOVEMENT_MODE_FOLLOW = 2 + }; enum ComputerCameraMovementMode { - COMPUTER_CAMERA_MOVEMENT_MODE_DEFAULT = 0, - COMPUTER_CAMERA_MOVEMENT_MODE_CLASSIC = 1, - COMPUTER_CAMERA_MOVEMENT_MODE_FOLLOW = 2}; - enum TouchMovementMode { - TOUCH_MOVEMENT_MODE_DEFAULT = 0, - TOUCH_MOVEMENT_MODE_THUMBSTICK = 1, - TOUCH_MOVEMENT_MODE_DPAD = 2, - TOUCH_MOVEMENT_MODE_THUMBPAD = 3, - TOUCH_MOVEMENT_MODE_CLICK_TO_MOVE = 4 }; - enum ComputerMovementMode { - COMPUTER_MOVEMENT_MODE_DEFAULT = 0, - COMPUTER_MOVEMENT_MODE_KBD_MOUSE = 1, - COMPUTER_MOVEMENT_MODE_CLICK_TO_MOVE = 2}; + COMPUTER_CAMERA_MOVEMENT_MODE_DEFAULT = 0, + COMPUTER_CAMERA_MOVEMENT_MODE_CLASSIC = 1, + COMPUTER_CAMERA_MOVEMENT_MODE_FOLLOW = 2 + }; + enum TouchMovementMode { + TOUCH_MOVEMENT_MODE_DEFAULT = 0, + TOUCH_MOVEMENT_MODE_THUMBSTICK = 1, + TOUCH_MOVEMENT_MODE_DPAD = 2, + TOUCH_MOVEMENT_MODE_THUMBPAD = 3, + TOUCH_MOVEMENT_MODE_CLICK_TO_MOVE = 4 + }; + enum ComputerMovementMode { + COMPUTER_MOVEMENT_MODE_DEFAULT = 0, + COMPUTER_MOVEMENT_MODE_KBD_MOUSE = 1, + COMPUTER_MOVEMENT_MODE_CLICK_TO_MOVE = 2 + }; enum YearSettings { Year2016 = 0, Year2015 = 1, @@ -40,7 +44,8 @@ namespace RBX }; enum RotationType { ROTATION_TYPE_MOVEMENT_RELATIVE = 0, - ROTATION_TYPE_CAMERA_RELATIVE = 1}; + ROTATION_TYPE_CAMERA_RELATIVE = 1 + }; static Reflection::PropDescriptor prop_masterVolume; @@ -88,14 +93,14 @@ namespace RBX void setFreeLook(bool canLook) { freeLook = canLook; } bool getFreeLook() { return freeLook; } - bool inClassicMode() { return controlMode == CONTROL_CLASSIC; } - bool inMouseLockMode() { return controlMode == CONTROL_MOUSELOCK; } - bool inHybridMode() { return controlMode == CONTROL_HYBRID; } - bool inCamlockMode() { return controlMode == CONTROL_CAMLOCK; } - bool inMousepanMode() { return controlMode == CONTROL_MOUSEPAN; } - - bool mouseLockedInMouseLockMode() { return inMouseLockMode() && isMouseLocked(); } - bool camLockedInCamLockMode() { return inCamlockMode() && !getFreeLook(); } + bool inClassicMode() { return controlMode == CONTROL_CLASSIC; } + bool inMouseLockMode() { return controlMode == CONTROL_MOUSELOCK; } + bool inHybridMode() { return controlMode == CONTROL_HYBRID; } + bool inCamlockMode() { return controlMode == CONTROL_CAMLOCK; } + bool inMousepanMode() { return controlMode == CONTROL_MOUSEPAN; } + + bool mouseLockedInMouseLockMode() { return inMouseLockMode() && isMouseLocked(); } + bool camLockedInCamLockMode() { return inCamlockMode() && !getFreeLook(); } bool getTutorialState(std::string tutorialId); void setTutorialState(std::string tutorialId, bool value); @@ -118,11 +123,11 @@ namespace RBX bool getFullScreenConst() const { return fullscreen; } bool getFullScreen() { return fullscreen; } - void setFullScreen(bool value) - { - if(value != fullscreen) + void setFullScreen(bool value) + { + if (value != fullscreen) { - fullscreen = value; + fullscreen = value; fullscreenChangedSignal(value); } } @@ -143,12 +148,12 @@ namespace RBX void setMouseSensitivity(float value); bool inStudioMode() { return studio; } - void setStudioMode(bool value) + void setStudioMode(bool value) { - if(value != studio) + if (value != studio) { studioModeChangedSignal(value); - studio = value; + studio = value; } } @@ -157,7 +162,7 @@ namespace RBX std::string getGoogleAnalyticsClientId() const; void setGoogleAnalyticsClientId(const std::string& id); - + /*override*/ void reset(); /*override*/ void verifySetParent(const Instance* instance) const; @@ -166,7 +171,7 @@ namespace RBX rbx::signal fullscreenChangedSignal; rbx::signal studioModeChangedSignal; - private: + private: ControlMode controlMode; RenderQualitySetting renderQualitySetting; YearSettings currentYear; @@ -205,4 +210,4 @@ namespace RBX std::string googleAnalyticsClientId; }; -} +} \ No newline at end of file diff --git a/App/lua/LuaBridge.h b/App/lua/LuaBridge.h new file mode 100644 index 0000000..c33537b --- /dev/null +++ b/App/lua/LuaBridge.h @@ -0,0 +1,264 @@ +#pragma once + +#include "Lua.hpp" + +#include "util/exception.h" +#include "util/utilities.h" +#include "rbx/Debug.h" +#include "rbx/atomic.h" + +namespace RBX { namespace Lua { + +void newweaktable(lua_State *L, const char *mode); + +template +int memberFunctionProxy(lua_State* thread) +{ + C* c = reinterpret_cast(lua_touserdata(thread, lua_upvalueindex(1))); + return (c->*Func)(thread); +} + +template +void pushMemberFunction(lua_State* L, C* c) +{ + lua_pushlightuserdata(L, (void*)c); + lua_pushcclosure(L, &memberFunctionProxy, 1); +} + +// TODO: Use traits pattern rather than bool __eq +template +class Bridge +{ +public: + // 0 arg constructor + static Class* pushNewObject(lua_State *L) + { + Class* data = (Class*)lua_newuserdata(L, sizeof(Class)); + new(data) Class; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return data; + } + // 1 arg constructor + template + static Class* pushNewObject(lua_State *L, Param1 param1) + { + Class* data = (Class*)lua_newuserdata(L, sizeof(Class)); + new(data) Class(param1); + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return data; + } + // 2 arg constructor + template + static Class* pushNewObject(lua_State *L, Param1 param1, Param2 param2) + { + Class* data = (Class*)lua_newuserdata(L, sizeof(Class)); + new(data) Class(param1, param2); + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return data; + } + + // Throws an exception if index doesn't hold the right type + static Class& getObject(lua_State *L, unsigned int index) { + void *ud = luaL_checkudata(L, index, className); + return *reinterpret_cast(ud); + } + + // Returns false if index doesn't hold the right type (leaving value unchanged) + template + static bool getValue(lua_State *L, unsigned int index, V& value) { + + // A re-implementation of luaL_checkudata that doesn't throw an exception + void *p = lua_touserdata(L, index); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, index)) { /* does it have a metatable? */ + lua_getfield(L, LUA_REGISTRYINDEX, className); /* get correct metatable */ + if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ + lua_pop(L, 2); /* remove both metatables */ + value = *reinterpret_cast(p); + return true; + } + else + lua_pop(L, 2); /* remove both metatables */ + } + } + return false; + } + + static void registerClass (lua_State *L); + +/// gcc craps out with the error while it is called from ScriptContext.cpp for registerClass fns for the Bridge e.g EventBridge::registerClass(globalState); +// error: 'static int RBX::Lua::Bridge::on_index(lua_State*) [with Class = RBX::Lua::EventInstance, bool __eq = true]' is protected +#ifdef _WIN32 +protected: +#endif + + // The following members must be implemented by client: + static int on_index(const Class& object, const char* name, lua_State *L); + static void on_newindex(Class& object, const char* name, lua_State *L); + + // This member may be specialized when StringConverter has no implementation for Class: + static int on_tostring(const Class& object, lua_State *L); + + static const char* className; + + static int on_gc(lua_State *L) { + getObject(L, 1).~Class(); + return 0; + } + + static int on_tostring(lua_State *L) { + return on_tostring(getObject(L, 1), L); + } + + static int on_newindex(lua_State *L) { + const char* name = lua_checkstring_secure(L, 2); + on_newindex(getObject(L, 1), name, L); + return 0; + } + + static int on_index(lua_State *L) { + const char* name = lua_checkstring_secure(L, 2); + return on_index(getObject(L, 1), name, L); + } + + static int on_eq(lua_State *L) { + lua_pushboolean(L, getObject(L, 1) == getObject(L, 2)); + return 1; + } +}; + +// This class hides parent on purpose +template +class SharedPtrBridge : protected Bridge, false> +{ +public: + static void registerClass (lua_State *L) + { + Bridge, false>::registerClass(L); + } + + static void registerClassLibrary (lua_State *L) { + // Declare the UserData re-use table + lua_pushlightuserdata(L, (void*)&push); + newweaktable(L, "v"); + lua_rawset(L, LUA_REGISTRYINDEX); + } + + static void push(lua_State *L, boost::shared_ptr instance) + { + if (instance==NULL) + lua_pushnil(L); + else + { +#ifdef _DEBUG + int i = lua_gettop(L); +#endif + // Matt Campbell at Serotek Corporation had a great idea. Create only + // a single instance of the userdata and re-use it. This way the userdata + // can be used in table keys, and we don't need an __eq operator + // Also see http://lua-users.org/lists/lua-l/2004-07/msg00391.html + + lua_pushlightuserdata(L, (void*)&push ); /* Registry mapping for weak table. Key is arbitrary. */ + lua_rawget(L, LUA_REGISTRYINDEX); // Stack: t + RBXASSERT(!lua_isnil( L, -1 )); // Did you forget to call registerClassLibrary?? + + // Now the top of the stack is our lookup table + // See if we already have a UserData for this instance + lua_pushlightuserdata (L, (void*)instance.get()); // Stack: t, i + lua_rawget (L, -2); // Stack: t, t[i] + + if( lua_isnil( L, -1 ) ) { // Stack: t, nil or I + lua_pop (L, 1); // Stack: t + Bridge, false>::pushNewObject(L, instance); // Stack: t, I + /* store the value for later use */ + lua_pushlightuserdata (L, (void*)instance.get()); // Stack: t, I, i + lua_pushvalue (L, -2); // Stack: t, I, i, I + lua_rawset (L, -4); // Stack: t, I + } + lua_remove (L, -2); // Stack: I +#ifdef _DEBUG + RBXASSERT(lua_gettop(L) == i + 1); +#endif + } + } + + static boost::shared_ptr getPtr(lua_State *L, unsigned int index) + { + if (lua_isnil(L, index)) + return boost::shared_ptr(); + else + return RBX::Lua::Bridge, false>::getObject(L, index); + } + + template + static bool getPtr(lua_State *L, unsigned int index, V& value) { + if (lua_isnil(L, index)) + { + value = boost::shared_ptr(); + return true; + } + else + return Bridge, false>::getValue(L, index, value); + } + +}; + +// This class hides Bridge on purpose +template +class SingletonBridge : protected Bridge +{ +public: + static void registerClass (lua_State *L) { + Bridge::registerClass(L); + } + + static void registerClassLibrary (lua_State *L) { + // Declare the UserData re-use table + lua_pushlightuserdata(L, (void*)&push); + lua_newtable(L); // Not a weak table, unlike SharedPtrBridge + lua_rawset(L, LUA_REGISTRYINDEX); + } + + static void push(lua_State *L, T item) + { +#ifdef _DEBUG + int i = lua_gettop(L); +#endif + // Matt Campbell at Serotek Corporation had a great idea. Create only + // a single instance of the userdata and re-use it. This way the userdata + // can be used in table keys + // Also see http://lua-users.org/lists/lua-l/2004-07/msg00391.html + + lua_pushlightuserdata(L, (void*)&push); /* Registry mapping for table. Key is arbitrary. */ + lua_rawget(L, LUA_REGISTRYINDEX); // Stack: t + RBXASSERT(!lua_isnil( L, -1 )); // Did you forget to call registerClassLibrary?? + + // Now the top of the stack is our lookup table + // See if we already have a UserData for this instance + lua_pushlightuserdata (L, (void*)item); // Stack: t, i + lua_rawget (L, -2); // Stack: t, t[i] + + // TODO: only allow explicit, one-time declaration + if( lua_isnil( L, -1 ) ) { // Stack: t, nil or I + lua_pop (L, 1); // Stack: t + Bridge::pushNewObject(L, item); // Stack: t, I + /* store the value for later use */ + lua_pushlightuserdata (L, (void*)item); // Stack: t, I, i + lua_pushvalue (L, -2); // Stack: t, I, i, I + lua_rawset (L, -4); // Stack: t, I + } + lua_remove (L, -2); // Stack: I + +#ifdef _DEBUG + RBXASSERT(lua_gettop(L) == i + 1); +#endif + } +}; + +} } + + + diff --git a/App/lua/lua.hpp b/App/lua/lua.hpp new file mode 100644 index 0000000..e64130a --- /dev/null +++ b/App/lua/lua.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +namespace RBX +{ + namespace Lua + { + extern const char* safe_lua_tostring(lua_State *L, int idx); + extern const char* throwable_lua_tostring(lua_State *L, int idx); + extern float lua_tofloat(lua_State *L, int idx); + extern void protect_metatable(lua_State* thread, int index); + inline void lua_pushstring(lua_State* thread, const std::string& s) + { + lua_pushlstring(thread, s.c_str(), s.size()); + } + + const char* lua_checkstring_secure(lua_State* L, int idx); + + void lua_resetstack(lua_State* L, int idx); + + // Pops items from the stack when it goes out of scope + class ScopedPopper + { + int popCount; + lua_State* const thread; + public: + ScopedPopper(lua_State* thread, int popCount) + :thread(thread),popCount(popCount) + {} + + ScopedPopper& operator +=(int popCount) + { + this->popCount += popCount; + return *this; + } + + ScopedPopper& operator -=(int popCount) + { + this->popCount -= popCount; + return *this; + } + + ~ScopedPopper() + { + lua_pop(thread, popCount); + } + }; + + class ScopedState + { + lua_State* const thread; + public: + ScopedState() + :thread(luaL_newstate()) + {} + + ~ScopedState() + { + lua_close(thread); + } + + operator lua_State*() + { + return thread; + } + }; + + } + +} diff --git a/App/lua/luaStubs0.h b/App/lua/luaStubs0.h new file mode 100644 index 0000000..37dcb58 --- /dev/null +++ b/App/lua/luaStubs0.h @@ -0,0 +1,103 @@ +#pragma once +#include "lua/lua.hpp" + +RBX::Lua::LuaStubGen<609> stub609; +RBX::Lua::LuaStubGen<288> stub288; +RBX::Lua::LuaStubGen<646> stub646; +RBX::Lua::LuaStubGen<387> stub387; +RBX::Lua::LuaStubGen<649> stub649; +RBX::Lua::LuaStubGen<687> stub687; +RBX::Lua::LuaStubGen<836> stub836; +RBX::Lua::LuaStubGen<959> stub959; +RBX::Lua::LuaStubGen<803> stub803; +RBX::Lua::LuaStubGen<107> stub107; +RBX::Lua::LuaStubGen<361> stub361; +RBX::Lua::LuaStubGen<172> stub172; +RBX::Lua::LuaStubGen<666> stub666; +RBX::Lua::LuaStubGen<901> stub901; +RBX::Lua::LuaStubGen<287> stub287; +RBX::Lua::LuaStubGen<523> stub523; +RBX::Lua::LuaStubGen<701> stub701; +RBX::Lua::LuaStubGen<316> stub316; +RBX::Lua::LuaStubGen<671> stub671; +RBX::Lua::LuaStubGen<423> stub423; +RBX::Lua::LuaStubGen<718> stub718; +RBX::Lua::LuaStubGen<711> stub711; +RBX::Lua::LuaStubGen<211> stub211; +RBX::Lua::LuaStubGen<158> stub158; +RBX::Lua::LuaStubGen<219> stub219; +RBX::Lua::LuaStubGen<68> stub68; +RBX::Lua::LuaStubGen<790> stub790; +RBX::Lua::LuaStubGen<58> stub58; +RBX::Lua::LuaStubGen<919> stub919; +RBX::Lua::LuaStubGen<747> stub747; +RBX::Lua::LuaStubGen<161> stub161; +RBX::Lua::LuaStubGen<119> stub119; +RBX::Lua::LuaStubGen<622> stub622; +RBX::Lua::LuaStubGen<183> stub183; +RBX::Lua::LuaStubGen<93> stub93; +RBX::Lua::LuaStubGen<560> stub560; +RBX::Lua::LuaStubGen<382> stub382; +RBX::Lua::LuaStubGen<455> stub455; +RBX::Lua::LuaStubGen<562> stub562; +RBX::Lua::LuaStubGen<554> stub554; +RBX::Lua::LuaStubGen<652> stub652; +RBX::Lua::LuaStubGen<997> stub997; +RBX::Lua::LuaStubGen<237> stub237; +RBX::Lua::LuaStubGen<655> stub655; +RBX::Lua::LuaStubGen<892> stub892; +RBX::Lua::LuaStubGen<840> stub840; +RBX::Lua::LuaStubGen<872> stub872; +RBX::Lua::LuaStubGen<821> stub821; +RBX::Lua::LuaStubGen<314> stub314; +RBX::Lua::LuaStubGen<249> stub249; +RBX::Lua::LuaStubGen<82> stub82; +RBX::Lua::LuaStubGen<470> stub470; +RBX::Lua::LuaStubGen<203> stub203; +RBX::Lua::LuaStubGen<722> stub722; +RBX::Lua::LuaStubGen<492> stub492; +RBX::Lua::LuaStubGen<29> stub29; +RBX::Lua::LuaStubGen<429> stub429; +RBX::Lua::LuaStubGen<933> stub933; +RBX::Lua::LuaStubGen<983> stub983; +RBX::Lua::LuaStubGen<700> stub700; +RBX::Lua::LuaStubGen<471> stub471; +RBX::Lua::LuaStubGen<453> stub453; +RBX::Lua::LuaStubGen<396> stub396; +RBX::Lua::LuaStubGen<330> stub330; +RBX::Lua::LuaStubGen<206> stub206; +RBX::Lua::LuaStubGen<362> stub362; +RBX::Lua::LuaStubGen<737> stub737; +RBX::Lua::LuaStubGen<390> stub390; +RBX::Lua::LuaStubGen<914> stub914; +RBX::Lua::LuaStubGen<985> stub985; +RBX::Lua::LuaStubGen<490> stub490; +RBX::Lua::LuaStubGen<786> stub786; +RBX::Lua::LuaStubGen<173> stub173; +RBX::Lua::LuaStubGen<346> stub346; +RBX::Lua::LuaStubGen<643> stub643; +RBX::Lua::LuaStubGen<331> stub331; +RBX::Lua::LuaStubGen<539> stub539; +RBX::Lua::LuaStubGen<134> stub134; +RBX::Lua::LuaStubGen<325> stub325; +RBX::Lua::LuaStubGen<34> stub34; +RBX::Lua::LuaStubGen<88> stub88; +RBX::Lua::LuaStubGen<536> stub536; +RBX::Lua::LuaStubGen<79> stub79; +RBX::Lua::LuaStubGen<459> stub459; +RBX::Lua::LuaStubGen<842> stub842; +RBX::Lua::LuaStubGen<370> stub370; +RBX::Lua::LuaStubGen<553> stub553; +RBX::Lua::LuaStubGen<486> stub486; +RBX::Lua::LuaStubGen<591> stub591; +RBX::Lua::LuaStubGen<797> stub797; +RBX::Lua::LuaStubGen<571> stub571; +RBX::Lua::LuaStubGen<907> stub907; +RBX::Lua::LuaStubGen<43> stub43; +RBX::Lua::LuaStubGen<918> stub918; +RBX::Lua::LuaStubGen<866> stub866; +RBX::Lua::LuaStubGen<856> stub856; +RBX::Lua::LuaStubGen<751> stub751; +RBX::Lua::LuaStubGen<124> stub124; +RBX::Lua::LuaStubGen<559> stub559; +RBX::Lua::LuaStubGen<620> stub620; diff --git a/App/lua/luaStubs1.h b/App/lua/luaStubs1.h new file mode 100644 index 0000000..55d6a9c --- /dev/null +++ b/App/lua/luaStubs1.h @@ -0,0 +1,103 @@ +#pragma once +#include "lua/lua.hpp" + +RBX::Lua::LuaStubGen<339> stub339; +RBX::Lua::LuaStubGen<510> stub510; +RBX::Lua::LuaStubGen<634> stub634; +RBX::Lua::LuaStubGen<17> stub17; +RBX::Lua::LuaStubGen<302> stub302; +RBX::Lua::LuaStubGen<454> stub454; +RBX::Lua::LuaStubGen<814> stub814; +RBX::Lua::LuaStubGen<887> stub887; +RBX::Lua::LuaStubGen<824> stub824; +RBX::Lua::LuaStubGen<703> stub703; +RBX::Lua::LuaStubGen<932> stub932; +RBX::Lua::LuaStubGen<220> stub220; +RBX::Lua::LuaStubGen<972> stub972; +RBX::Lua::LuaStubGen<99> stub99; +RBX::Lua::LuaStubGen<627> stub627; +RBX::Lua::LuaStubGen<13> stub13; +RBX::Lua::LuaStubGen<633> stub633; +RBX::Lua::LuaStubGen<800> stub800; +RBX::Lua::LuaStubGen<875> stub875; +RBX::Lua::LuaStubGen<458> stub458; +RBX::Lua::LuaStubGen<181> stub181; +RBX::Lua::LuaStubGen<352> stub352; +RBX::Lua::LuaStubGen<920> stub920; +RBX::Lua::LuaStubGen<951> stub951; +RBX::Lua::LuaStubGen<319> stub319; +RBX::Lua::LuaStubGen<716> stub716; +RBX::Lua::LuaStubGen<529> stub529; +RBX::Lua::LuaStubGen<507> stub507; +RBX::Lua::LuaStubGen<85> stub85; +RBX::Lua::LuaStubGen<105> stub105; +RBX::Lua::LuaStubGen<727> stub727; +RBX::Lua::LuaStubGen<568> stub568; +RBX::Lua::LuaStubGen<749> stub749; +RBX::Lua::LuaStubGen<530> stub530; +RBX::Lua::LuaStubGen<207> stub207; +RBX::Lua::LuaStubGen<5> stub5; +RBX::Lua::LuaStubGen<413> stub413; +RBX::Lua::LuaStubGen<675> stub675; +RBX::Lua::LuaStubGen<462> stub462; +RBX::Lua::LuaStubGen<762> stub762; +RBX::Lua::LuaStubGen<397> stub397; +RBX::Lua::LuaStubGen<431> stub431; +RBX::Lua::LuaStubGen<47> stub47; +RBX::Lua::LuaStubGen<307> stub307; +RBX::Lua::LuaStubGen<692> stub692; +RBX::Lua::LuaStubGen<320> stub320; +RBX::Lua::LuaStubGen<947> stub947; +RBX::Lua::LuaStubGen<775> stub775; +RBX::Lua::LuaStubGen<647> stub647; +RBX::Lua::LuaStubGen<766> stub766; +RBX::Lua::LuaStubGen<661> stub661; +RBX::Lua::LuaStubGen<384> stub384; +RBX::Lua::LuaStubGen<235> stub235; +RBX::Lua::LuaStubGen<177> stub177; +RBX::Lua::LuaStubGen<535> stub535; +RBX::Lua::LuaStubGen<580> stub580; +RBX::Lua::LuaStubGen<854> stub854; +RBX::Lua::LuaStubGen<987> stub987; +RBX::Lua::LuaStubGen<891> stub891; +RBX::Lua::LuaStubGen<846> stub846; +RBX::Lua::LuaStubGen<630> stub630; +RBX::Lua::LuaStubGen<491> stub491; +RBX::Lua::LuaStubGen<573> stub573; +RBX::Lua::LuaStubGen<488> stub488; +RBX::Lua::LuaStubGen<695> stub695; +RBX::Lua::LuaStubGen<995> stub995; +RBX::Lua::LuaStubGen<321> stub321; +RBX::Lua::LuaStubGen<322> stub322; +RBX::Lua::LuaStubGen<504> stub504; +RBX::Lua::LuaStubGen<403> stub403; +RBX::Lua::LuaStubGen<197> stub197; +RBX::Lua::LuaStubGen<487> stub487; +RBX::Lua::LuaStubGen<231> stub231; +RBX::Lua::LuaStubGen<236> stub236; +RBX::Lua::LuaStubGen<128> stub128; +RBX::Lua::LuaStubGen<381> stub381; +RBX::Lua::LuaStubGen<705> stub705; +RBX::Lua::LuaStubGen<61> stub61; +RBX::Lua::LuaStubGen<214> stub214; +RBX::Lua::LuaStubGen<497> stub497; +RBX::Lua::LuaStubGen<232> stub232; +RBX::Lua::LuaStubGen<725> stub725; +RBX::Lua::LuaStubGen<371> stub371; +RBX::Lua::LuaStubGen<401> stub401; +RBX::Lua::LuaStubGen<526> stub526; +RBX::Lua::LuaStubGen<448> stub448; +RBX::Lua::LuaStubGen<961> stub961; +RBX::Lua::LuaStubGen<97> stub97; +RBX::Lua::LuaStubGen<375> stub375; +RBX::Lua::LuaStubGen<979> stub979; +RBX::Lua::LuaStubGen<908> stub908; +RBX::Lua::LuaStubGen<227> stub227; +RBX::Lua::LuaStubGen<112> stub112; +RBX::Lua::LuaStubGen<819> stub819; +RBX::Lua::LuaStubGen<208> stub208; +RBX::Lua::LuaStubGen<78> stub78; +RBX::Lua::LuaStubGen<992> stub992; +RBX::Lua::LuaStubGen<209> stub209; +RBX::Lua::LuaStubGen<881> stub881; +RBX::Lua::LuaStubGen<463> stub463; diff --git a/App/lua/luaStubs2.h b/App/lua/luaStubs2.h new file mode 100644 index 0000000..5cc4474 --- /dev/null +++ b/App/lua/luaStubs2.h @@ -0,0 +1,103 @@ +#pragma once +#include "lua/lua.hpp" + +RBX::Lua::LuaStubGen<925> stub925; +RBX::Lua::LuaStubGen<311> stub311; +RBX::Lua::LuaStubGen<801> stub801; +RBX::Lua::LuaStubGen<450> stub450; +RBX::Lua::LuaStubGen<469> stub469; +RBX::Lua::LuaStubGen<522> stub522; +RBX::Lua::LuaStubGen<412> stub412; +RBX::Lua::LuaStubGen<418> stub418; +RBX::Lua::LuaStubGen<879> stub879; +RBX::Lua::LuaStubGen<745> stub745; +RBX::Lua::LuaStubGen<818> stub818; +RBX::Lua::LuaStubGen<69> stub69; +RBX::Lua::LuaStubGen<122> stub122; +RBX::Lua::LuaStubGen<654> stub654; +RBX::Lua::LuaStubGen<385> stub385; +RBX::Lua::LuaStubGen<160> stub160; +RBX::Lua::LuaStubGen<550> stub550; +RBX::Lua::LuaStubGen<199> stub199; +RBX::Lua::LuaStubGen<897> stub897; +RBX::Lua::LuaStubGen<60> stub60; +RBX::Lua::LuaStubGen<420> stub420; +RBX::Lua::LuaStubGen<712> stub712; +RBX::Lua::LuaStubGen<581> stub581; +RBX::Lua::LuaStubGen<613> stub613; +RBX::Lua::LuaStubGen<928> stub928; +RBX::Lua::LuaStubGen<391> stub391; +RBX::Lua::LuaStubGen<868> stub868; +RBX::Lua::LuaStubGen<517> stub517; +RBX::Lua::LuaStubGen<393> stub393; +RBX::Lua::LuaStubGen<593> stub593; +RBX::Lua::LuaStubGen<865> stub865; +RBX::Lua::LuaStubGen<610> stub610; +RBX::Lua::LuaStubGen<289> stub289; +RBX::Lua::LuaStubGen<508> stub508; +RBX::Lua::LuaStubGen<779> stub779; +RBX::Lua::LuaStubGen<224> stub224; +RBX::Lua::LuaStubGen<527> stub527; +RBX::Lua::LuaStubGen<733> stub733; +RBX::Lua::LuaStubGen<419> stub419; +RBX::Lua::LuaStubGen<106> stub106; +RBX::Lua::LuaStubGen<501> stub501; +RBX::Lua::LuaStubGen<677> stub677; +RBX::Lua::LuaStubGen<212> stub212; +RBX::Lua::LuaStubGen<859> stub859; +RBX::Lua::LuaStubGen<697> stub697; +RBX::Lua::LuaStubGen<357> stub357; +RBX::Lua::LuaStubGen<233> stub233; +RBX::Lua::LuaStubGen<248> stub248; +RBX::Lua::LuaStubGen<698> stub698; +RBX::Lua::LuaStubGen<552> stub552; +RBX::Lua::LuaStubGen<19> stub19; +RBX::Lua::LuaStubGen<873> stub873; +RBX::Lua::LuaStubGen<169> stub169; +RBX::Lua::LuaStubGen<367> stub367; +RBX::Lua::LuaStubGen<763> stub763; +RBX::Lua::LuaStubGen<115> stub115; +RBX::Lua::LuaStubGen<477> stub477; +RBX::Lua::LuaStubGen<475> stub475; +RBX::Lua::LuaStubGen<341> stub341; +RBX::Lua::LuaStubGen<686> stub686; +RBX::Lua::LuaStubGen<939> stub939; +RBX::Lua::LuaStubGen<648> stub648; +RBX::Lua::LuaStubGen<277> stub277; +RBX::Lua::LuaStubGen<584> stub584; +RBX::Lua::LuaStubGen<776> stub776; +RBX::Lua::LuaStubGen<684> stub684; +RBX::Lua::LuaStubGen<155> stub155; +RBX::Lua::LuaStubGen<51> stub51; +RBX::Lua::LuaStubGen<994> stub994; +RBX::Lua::LuaStubGen<702> stub702; +RBX::Lua::LuaStubGen<3> stub3; +RBX::Lua::LuaStubGen<73> stub73; +RBX::Lua::LuaStubGen<579> stub579; +RBX::Lua::LuaStubGen<83> stub83; +RBX::Lua::LuaStubGen<768> stub768; +RBX::Lua::LuaStubGen<813> stub813; +RBX::Lua::LuaStubGen<604> stub604; +RBX::Lua::LuaStubGen<312> stub312; +RBX::Lua::LuaStubGen<588> stub588; +RBX::Lua::LuaStubGen<834> stub834; +RBX::Lua::LuaStubGen<313> stub313; +RBX::Lua::LuaStubGen<967> stub967; +RBX::Lua::LuaStubGen<784> stub784; +RBX::Lua::LuaStubGen<139> stub139; +RBX::Lua::LuaStubGen<555> stub555; +RBX::Lua::LuaStubGen<157> stub157; +RBX::Lua::LuaStubGen<226> stub226; +RBX::Lua::LuaStubGen<263> stub263; +RBX::Lua::LuaStubGen<863> stub863; +RBX::Lua::LuaStubGen<882> stub882; +RBX::Lua::LuaStubGen<110> stub110; +RBX::Lua::LuaStubGen<274> stub274; +RBX::Lua::LuaStubGen<736> stub736; +RBX::Lua::LuaStubGen<113> stub113; +RBX::Lua::LuaStubGen<484> stub484; +RBX::Lua::LuaStubGen<638> stub638; +RBX::Lua::LuaStubGen<49> stub49; +RBX::Lua::LuaStubGen<774> stub774; +RBX::Lua::LuaStubGen<742> stub742; +RBX::Lua::LuaStubGen<485> stub485; diff --git a/App/lua/luaStubs3.h b/App/lua/luaStubs3.h new file mode 100644 index 0000000..bebaff7 --- /dev/null +++ b/App/lua/luaStubs3.h @@ -0,0 +1,103 @@ +#pragma once +#include "lua/lua.hpp" + +RBX::Lua::LuaStubGen<290> stub290; +RBX::Lua::LuaStubGen<734> stub734; +RBX::Lua::LuaStubGen<543> stub543; +RBX::Lua::LuaStubGen<433> stub433; +RBX::Lua::LuaStubGen<871> stub871; +RBX::Lua::LuaStubGen<308> stub308; +RBX::Lua::LuaStubGen<685> stub685; +RBX::Lua::LuaStubGen<839> stub839; +RBX::Lua::LuaStubGen<870> stub870; +RBX::Lua::LuaStubGen<910> stub910; +RBX::Lua::LuaStubGen<150> stub150; +RBX::Lua::LuaStubGen<439> stub439; +RBX::Lua::LuaStubGen<799> stub799; +RBX::Lua::LuaStubGen<760> stub760; +RBX::Lua::LuaStubGen<878> stub878; +RBX::Lua::LuaStubGen<912> stub912; +RBX::Lua::LuaStubGen<168> stub168; +RBX::Lua::LuaStubGen<743> stub743; +RBX::Lua::LuaStubGen<624> stub624; +RBX::Lua::LuaStubGen<516> stub516; +RBX::Lua::LuaStubGen<545> stub545; +RBX::Lua::LuaStubGen<148> stub148; +RBX::Lua::LuaStubGen<121> stub121; +RBX::Lua::LuaStubGen<977> stub977; +RBX::Lua::LuaStubGen<998> stub998; +RBX::Lua::LuaStubGen<156> stub156; +RBX::Lua::LuaStubGen<999> stub999; +RBX::Lua::LuaStubGen<860> stub860; +RBX::Lua::LuaStubGen<41> stub41; +RBX::Lua::LuaStubGen<479> stub479; +RBX::Lua::LuaStubGen<366> stub366; +RBX::Lua::LuaStubGen<938> stub938; +RBX::Lua::LuaStubGen<577> stub577; +RBX::Lua::LuaStubGen<640> stub640; +RBX::Lua::LuaStubGen<956> stub956; +RBX::Lua::LuaStubGen<929> stub929; +RBX::Lua::LuaStubGen<574> stub574; +RBX::Lua::LuaStubGen<619> stub619; +RBX::Lua::LuaStubGen<166> stub166; +RBX::Lua::LuaStubGen<198> stub198; +RBX::Lua::LuaStubGen<11> stub11; +RBX::Lua::LuaStubGen<64> stub64; +RBX::Lua::LuaStubGen<221> stub221; +RBX::Lua::LuaStubGen<564> stub564; +RBX::Lua::LuaStubGen<56> stub56; +RBX::Lua::LuaStubGen<787> stub787; +RBX::Lua::LuaStubGen<713> stub713; +RBX::Lua::LuaStubGen<777> stub777; +RBX::Lua::LuaStubGen<541> stub541; +RBX::Lua::LuaStubGen<187> stub187; +RBX::Lua::LuaStubGen<600> stub600; +RBX::Lua::LuaStubGen<472> stub472; +RBX::Lua::LuaStubGen<196> stub196; +RBX::Lua::LuaStubGen<323> stub323; +RBX::Lua::LuaStubGen<616> stub616; +RBX::Lua::LuaStubGen<242> stub242; +RBX::Lua::LuaStubGen<699> stub699; +RBX::Lua::LuaStubGen<678> stub678; +RBX::Lua::LuaStubGen<532> stub532; +RBX::Lua::LuaStubGen<949> stub949; +RBX::Lua::LuaStubGen<973> stub973; +RBX::Lua::LuaStubGen<270> stub270; +RBX::Lua::LuaStubGen<14> stub14; +RBX::Lua::LuaStubGen<451> stub451; +RBX::Lua::LuaStubGen<12> stub12; +RBX::Lua::LuaStubGen<809> stub809; +RBX::Lua::LuaStubGen<642> stub642; +RBX::Lua::LuaStubGen<324> stub324; +RBX::Lua::LuaStubGen<7> stub7; +RBX::Lua::LuaStubGen<569> stub569; +RBX::Lua::LuaStubGen<880> stub880; +RBX::Lua::LuaStubGen<828> stub828; +RBX::Lua::LuaStubGen<35> stub35; +RBX::Lua::LuaStubGen<547> stub547; +RBX::Lua::LuaStubGen<447> stub447; +RBX::Lua::LuaStubGen<228> stub228; +RBX::Lua::LuaStubGen<739> stub739; +RBX::Lua::LuaStubGen<796> stub796; +RBX::Lua::LuaStubGen<889> stub889; +RBX::Lua::LuaStubGen<317> stub317; +RBX::Lua::LuaStubGen<668> stub668; +RBX::Lua::LuaStubGen<598> stub598; +RBX::Lua::LuaStubGen<781> stub781; +RBX::Lua::LuaStubGen<466> stub466; +RBX::Lua::LuaStubGen<823> stub823; +RBX::Lua::LuaStubGen<753> stub753; +RBX::Lua::LuaStubGen<101> stub101; +RBX::Lua::LuaStubGen<170> stub170; +RBX::Lua::LuaStubGen<612> stub612; +RBX::Lua::LuaStubGen<40> stub40; +RBX::Lua::LuaStubGen<502> stub502; +RBX::Lua::LuaStubGen<621> stub621; +RBX::Lua::LuaStubGen<893> stub893; +RBX::Lua::LuaStubGen<452> stub452; +RBX::Lua::LuaStubGen<345> stub345; +RBX::Lua::LuaStubGen<546> stub546; +RBX::Lua::LuaStubGen<623> stub623; +RBX::Lua::LuaStubGen<513> stub513; +RBX::Lua::LuaStubGen<15> stub15; +RBX::Lua::LuaStubGen<758> stub758; diff --git a/App/lua/luaStubs4.h b/App/lua/luaStubs4.h new file mode 100644 index 0000000..61dac43 --- /dev/null +++ b/App/lua/luaStubs4.h @@ -0,0 +1,103 @@ +#pragma once +#include "lua/lua.hpp" + +RBX::Lua::LuaStubGen<614> stub614; +RBX::Lua::LuaStubGen<481> stub481; +RBX::Lua::LuaStubGen<217> stub217; +RBX::Lua::LuaStubGen<883> stub883; +RBX::Lua::LuaStubGen<261> stub261; +RBX::Lua::LuaStubGen<432> stub432; +RBX::Lua::LuaStubGen<383> stub383; +RBX::Lua::LuaStubGen<791> stub791; +RBX::Lua::LuaStubGen<395> stub395; +RBX::Lua::LuaStubGen<20> stub20; +RBX::Lua::LuaStubGen<916> stub916; +RBX::Lua::LuaStubGen<229> stub229; +RBX::Lua::LuaStubGen<215> stub215; +RBX::Lua::LuaStubGen<589> stub589; +RBX::Lua::LuaStubGen<23> stub23; +RBX::Lua::LuaStubGen<750> stub750; +RBX::Lua::LuaStubGen<483> stub483; +RBX::Lua::LuaStubGen<724> stub724; +RBX::Lua::LuaStubGen<505> stub505; +RBX::Lua::LuaStubGen<254> stub254; +RBX::Lua::LuaStubGen<752> stub752; +RBX::Lua::LuaStubGen<926> stub926; +RBX::Lua::LuaStubGen<524> stub524; +RBX::Lua::LuaStubGen<672> stub672; +RBX::Lua::LuaStubGen<406> stub406; +RBX::Lua::LuaStubGen<924> stub924; +RBX::Lua::LuaStubGen<394> stub394; +RBX::Lua::LuaStubGen<506> stub506; +RBX::Lua::LuaStubGen<772> stub772; +RBX::Lua::LuaStubGen<603> stub603; +RBX::Lua::LuaStubGen<175> stub175; +RBX::Lua::LuaStubGen<256> stub256; +RBX::Lua::LuaStubGen<392> stub392; +RBX::Lua::LuaStubGen<605> stub605; +RBX::Lua::LuaStubGen<534> stub534; +RBX::Lua::LuaStubGen<443> stub443; +RBX::Lua::LuaStubGen<365> stub365; +RBX::Lua::LuaStubGen<126> stub126; +RBX::Lua::LuaStubGen<709> stub709; +RBX::Lua::LuaStubGen<42> stub42; +RBX::Lua::LuaStubGen<10> stub10; +RBX::Lua::LuaStubGen<292> stub292; +RBX::Lua::LuaStubGen<669> stub669; +RBX::Lua::LuaStubGen<282> stub282; +RBX::Lua::LuaStubGen<201> stub201; +RBX::Lua::LuaStubGen<585> stub585; +RBX::Lua::LuaStubGen<693> stub693; +RBX::Lua::LuaStubGen<241> stub241; +RBX::Lua::LuaStubGen<131> stub131; +RBX::Lua::LuaStubGen<70> stub70; +RBX::Lua::LuaStubGen<360> stub360; +RBX::Lua::LuaStubGen<537> stub537; +RBX::Lua::LuaStubGen<408> stub408; +RBX::Lua::LuaStubGen<515> stub515; +RBX::Lua::LuaStubGen<9> stub9; +RBX::Lua::LuaStubGen<111> stub111; +RBX::Lua::LuaStubGen<388> stub388; +RBX::Lua::LuaStubGen<937> stub937; +RBX::Lua::LuaStubGen<276> stub276; +RBX::Lua::LuaStubGen<426> stub426; +RBX::Lua::LuaStubGen<832> stub832; +RBX::Lua::LuaStubGen<90> stub90; +RBX::Lua::LuaStubGen<414> stub414; +RBX::Lua::LuaStubGen<151> stub151; +RBX::Lua::LuaStubGen<178> stub178; +RBX::Lua::LuaStubGen<625> stub625; +RBX::Lua::LuaStubGen<478> stub478; +RBX::Lua::LuaStubGen<941> stub941; +RBX::Lua::LuaStubGen<71> stub71; +RBX::Lua::LuaStubGen<556> stub556; +RBX::Lua::LuaStubGen<421> stub421; +RBX::Lua::LuaStubGen<683> stub683; +RBX::Lua::LuaStubGen<422> stub422; +RBX::Lua::LuaStubGen<193> stub193; +RBX::Lua::LuaStubGen<626> stub626; +RBX::Lua::LuaStubGen<738> stub738; +RBX::Lua::LuaStubGen<728> stub728; +RBX::Lua::LuaStubGen<438> stub438; +RBX::Lua::LuaStubGen<606> stub606; +RBX::Lua::LuaStubGen<969> stub969; +RBX::Lua::LuaStubGen<602> stub602; +RBX::Lua::LuaStubGen<857> stub857; +RBX::Lua::LuaStubGen<572> stub572; +RBX::Lua::LuaStubGen<129> stub129; +RBX::Lua::LuaStubGen<578> stub578; +RBX::Lua::LuaStubGen<544> stub544; +RBX::Lua::LuaStubGen<358> stub358; +RBX::Lua::LuaStubGen<651> stub651; +RBX::Lua::LuaStubGen<482> stub482; +RBX::Lua::LuaStubGen<729> stub729; +RBX::Lua::LuaStubGen<582> stub582; +RBX::Lua::LuaStubGen<975> stub975; +RBX::Lua::LuaStubGen<238> stub238; +RBX::Lua::LuaStubGen<167> stub167; +RBX::Lua::LuaStubGen<632> stub632; +RBX::Lua::LuaStubGen<770> stub770; +RBX::Lua::LuaStubGen<176> stub176; +RBX::Lua::LuaStubGen<805> stub805; +RBX::Lua::LuaStubGen<473> stub473; +RBX::Lua::LuaStubGen<145> stub145; diff --git a/App/lua/luaStubs5.h b/App/lua/luaStubs5.h new file mode 100644 index 0000000..6b030d8 --- /dev/null +++ b/App/lua/luaStubs5.h @@ -0,0 +1,103 @@ +#pragma once +#include "lua/lua.hpp" + +RBX::Lua::LuaStubGen<641> stub641; +RBX::Lua::LuaStubGen<200> stub200; +RBX::Lua::LuaStubGen<428> stub428; +RBX::Lua::LuaStubGen<596> stub596; +RBX::Lua::LuaStubGen<909> stub909; +RBX::Lua::LuaStubGen<540> stub540; +RBX::Lua::LuaStubGen<108> stub108; +RBX::Lua::LuaStubGen<328> stub328; +RBX::Lua::LuaStubGen<852> stub852; +RBX::Lua::LuaStubGen<81> stub81; +RBX::Lua::LuaStubGen<761> stub761; +RBX::Lua::LuaStubGen<159> stub159; +RBX::Lua::LuaStubGen<46> stub46; +RBX::Lua::LuaStubGen<272> stub272; +RBX::Lua::LuaStubGen<404> stub404; +RBX::Lua::LuaStubGen<386> stub386; +RBX::Lua::LuaStubGen<719> stub719; +RBX::Lua::LuaStubGen<191> stub191; +RBX::Lua::LuaStubGen<33> stub33; +RBX::Lua::LuaStubGen<851> stub851; +RBX::Lua::LuaStubGen<561> stub561; +RBX::Lua::LuaStubGen<681> stub681; +RBX::Lua::LuaStubGen<721> stub721; +RBX::Lua::LuaStubGen<87> stub87; +RBX::Lua::LuaStubGen<996> stub996; +RBX::Lua::LuaStubGen<576> stub576; +RBX::Lua::LuaStubGen<835> stub835; +RBX::Lua::LuaStubGen<305> stub305; +RBX::Lua::LuaStubGen<182> stub182; +RBX::Lua::LuaStubGen<974> stub974; +RBX::Lua::LuaStubGen<294> stub294; +RBX::Lua::LuaStubGen<922> stub922; +RBX::Lua::LuaStubGen<890> stub890; +RBX::Lua::LuaStubGen<185> stub185; +RBX::Lua::LuaStubGen<253> stub253; +RBX::Lua::LuaStubGen<298> stub298; +RBX::Lua::LuaStubGen<744> stub744; +RBX::Lua::LuaStubGen<861> stub861; +RBX::Lua::LuaStubGen<885> stub885; +RBX::Lua::LuaStubGen<351> stub351; +RBX::Lua::LuaStubGen<63> stub63; +RBX::Lua::LuaStubGen<327> stub327; +RBX::Lua::LuaStubGen<599> stub599; +RBX::Lua::LuaStubGen<720> stub720; +RBX::Lua::LuaStubGen<911> stub911; +RBX::Lua::LuaStubGen<676> stub676; +RBX::Lua::LuaStubGen<993> stub993; +RBX::Lua::LuaStubGen<326> stub326; +RBX::Lua::LuaStubGen<489> stub489; +RBX::Lua::LuaStubGen<4> stub4; +RBX::Lua::LuaStubGen<830> stub830; +RBX::Lua::LuaStubGen<398> stub398; +RBX::Lua::LuaStubGen<509> stub509; +RBX::Lua::LuaStubGen<372> stub372; +RBX::Lua::LuaStubGen<102> stub102; +RBX::Lua::LuaStubGen<296> stub296; +RBX::Lua::LuaStubGen<86> stub86; +RBX::Lua::LuaStubGen<258> stub258; +RBX::Lua::LuaStubGen<981> stub981; +RBX::Lua::LuaStubGen<971> stub971; +RBX::Lua::LuaStubGen<65> stub65; +RBX::Lua::LuaStubGen<467> stub467; +RBX::Lua::LuaStubGen<566> stub566; +RBX::Lua::LuaStubGen<118> stub118; +RBX::Lua::LuaStubGen<565> stub565; +RBX::Lua::LuaStubGen<717> stub717; +RBX::Lua::LuaStubGen<741> stub741; +RBX::Lua::LuaStubGen<66> stub66; +RBX::Lua::LuaStubGen<348> stub348; +RBX::Lua::LuaStubGen<557> stub557; +RBX::Lua::LuaStubGen<202> stub202; +RBX::Lua::LuaStubGen<674> stub674; +RBX::Lua::LuaStubGen<480> stub480; +RBX::Lua::LuaStubGen<794> stub794; +RBX::Lua::LuaStubGen<234> stub234; +RBX::Lua::LuaStubGen<353> stub353; +RBX::Lua::LuaStubGen<757> stub757; +RBX::Lua::LuaStubGen<844> stub844; +RBX::Lua::LuaStubGen<900> stub900; +RBX::Lua::LuaStubGen<587> stub587; +RBX::Lua::LuaStubGen<773> stub773; +RBX::Lua::LuaStubGen<84> stub84; +RBX::Lua::LuaStubGen<284> stub284; +RBX::Lua::LuaStubGen<300> stub300; +RBX::Lua::LuaStubGen<456> stub456; +RBX::Lua::LuaStubGen<461> stub461; +RBX::Lua::LuaStubGen<109> stub109; +RBX::Lua::LuaStubGen<349> stub349; +RBX::Lua::LuaStubGen<337> stub337; +RBX::Lua::LuaStubGen<154> stub154; +RBX::Lua::LuaStubGen<293> stub293; +RBX::Lua::LuaStubGen<706> stub706; +RBX::Lua::LuaStubGen<764> stub764; +RBX::Lua::LuaStubGen<930> stub930; +RBX::Lua::LuaStubGen<465> stub465; +RBX::Lua::LuaStubGen<245> stub245; +RBX::Lua::LuaStubGen<55> stub55; +RBX::Lua::LuaStubGen<960> stub960; +RBX::Lua::LuaStubGen<756> stub756; +RBX::Lua::LuaStubGen<521> stub521; diff --git a/App/lua/luaStubs6.h b/App/lua/luaStubs6.h new file mode 100644 index 0000000..ad7f12e --- /dev/null +++ b/App/lua/luaStubs6.h @@ -0,0 +1,103 @@ +#pragma once +#include "lua/lua.hpp" + +RBX::Lua::LuaStubGen<144> stub144; +RBX::Lua::LuaStubGen<44> stub44; +RBX::Lua::LuaStubGen<667> stub667; +RBX::Lua::LuaStubGen<896> stub896; +RBX::Lua::LuaStubGen<318> stub318; +RBX::Lua::LuaStubGen<731> stub731; +RBX::Lua::LuaStubGen<98> stub98; +RBX::Lua::LuaStubGen<306> stub306; +RBX::Lua::LuaStubGen<434> stub434; +RBX::Lua::LuaStubGen<374> stub374; +RBX::Lua::LuaStubGen<92> stub92; +RBX::Lua::LuaStubGen<309> stub309; +RBX::Lua::LuaStubGen<94> stub94; +RBX::Lua::LuaStubGen<239> stub239; +RBX::Lua::LuaStubGen<464> stub464; +RBX::Lua::LuaStubGen<243> stub243; +RBX::Lua::LuaStubGen<963> stub963; +RBX::Lua::LuaStubGen<514> stub514; +RBX::Lua::LuaStubGen<639> stub639; +RBX::Lua::LuaStubGen<816> stub816; +RBX::Lua::LuaStubGen<164> stub164; +RBX::Lua::LuaStubGen<653> stub653; +RBX::Lua::LuaStubGen<812> stub812; +RBX::Lua::LuaStubGen<269> stub269; +RBX::Lua::LuaStubGen<822> stub822; +RBX::Lua::LuaStubGen<864> stub864; +RBX::Lua::LuaStubGen<499> stub499; +RBX::Lua::LuaStubGen<130> stub130; +RBX::Lua::LuaStubGen<59> stub59; +RBX::Lua::LuaStubGen<27> stub27; +RBX::Lua::LuaStubGen<162> stub162; +RBX::Lua::LuaStubGen<570> stub570; +RBX::Lua::LuaStubGen<74> stub74; +RBX::Lua::LuaStubGen<117> stub117; +RBX::Lua::LuaStubGen<935> stub935; +RBX::Lua::LuaStubGen<96> stub96; +RBX::Lua::LuaStubGen<629> stub629; +RBX::Lua::LuaStubGen<163> stub163; +RBX::Lua::LuaStubGen<934> stub934; +RBX::Lua::LuaStubGen<275> stub275; +RBX::Lua::LuaStubGen<165> stub165; +RBX::Lua::LuaStubGen<267> stub267; +RBX::Lua::LuaStubGen<195> stub195; +RBX::Lua::LuaStubGen<833> stub833; +RBX::Lua::LuaStubGen<30> stub30; +RBX::Lua::LuaStubGen<304> stub304; +RBX::Lua::LuaStubGen<114> stub114; +RBX::Lua::LuaStubGen<369> stub369; +RBX::Lua::LuaStubGen<708> stub708; +RBX::Lua::LuaStubGen<611> stub611; +RBX::Lua::LuaStubGen<441> stub441; +RBX::Lua::LuaStubGen<512> stub512; +RBX::Lua::LuaStubGen<143> stub143; +RBX::Lua::LuaStubGen<615> stub615; +RBX::Lua::LuaStubGen<792> stub792; +RBX::Lua::LuaStubGen<76> stub76; +RBX::Lua::LuaStubGen<853> stub853; +RBX::Lua::LuaStubGen<500> stub500; +RBX::Lua::LuaStubGen<710> stub710; +RBX::Lua::LuaStubGen<782> stub782; +RBX::Lua::LuaStubGen<869> stub869; +RBX::Lua::LuaStubGen<690> stub690; +RBX::Lua::LuaStubGen<184> stub184; +RBX::Lua::LuaStubGen<424> stub424; +RBX::Lua::LuaStubGen<204> stub204; +RBX::Lua::LuaStubGen<2> stub2; +RBX::Lua::LuaStubGen<354> stub354; +RBX::Lua::LuaStubGen<400> stub400; +RBX::Lua::LuaStubGen<216> stub216; +RBX::Lua::LuaStubGen<152> stub152; +RBX::Lua::LuaStubGen<18> stub18; +RBX::Lua::LuaStubGen<190> stub190; +RBX::Lua::LuaStubGen<380> stub380; +RBX::Lua::LuaStubGen<246> stub246; +RBX::Lua::LuaStubGen<628> stub628; +RBX::Lua::LuaStubGen<26> stub26; +RBX::Lua::LuaStubGen<645> stub645; +RBX::Lua::LuaStubGen<945> stub945; +RBX::Lua::LuaStubGen<347> stub347; +RBX::Lua::LuaStubGen<829> stub829; +RBX::Lua::LuaStubGen<858> stub858; +RBX::Lua::LuaStubGen<802> stub802; +RBX::Lua::LuaStubGen<965> stub965; +RBX::Lua::LuaStubGen<262> stub262; +RBX::Lua::LuaStubGen<268> stub268; +RBX::Lua::LuaStubGen<806> stub806; +RBX::Lua::LuaStubGen<944> stub944; +RBX::Lua::LuaStubGen<225> stub225; +RBX::Lua::LuaStubGen<188> stub188; +RBX::Lua::LuaStubGen<266> stub266; +RBX::Lua::LuaStubGen<417> stub417; +RBX::Lua::LuaStubGen<377> stub377; +RBX::Lua::LuaStubGen<425> stub425; +RBX::Lua::LuaStubGen<286> stub286; +RBX::Lua::LuaStubGen<986> stub986; +RBX::Lua::LuaStubGen<843> stub843; +RBX::Lua::LuaStubGen<827> stub827; +RBX::Lua::LuaStubGen<807> stub807; +RBX::Lua::LuaStubGen<730> stub730; +RBX::Lua::LuaStubGen<440> stub440; diff --git a/App/lua/luaStubs7.h b/App/lua/luaStubs7.h new file mode 100644 index 0000000..7e03560 --- /dev/null +++ b/App/lua/luaStubs7.h @@ -0,0 +1,103 @@ +#pragma once +#include "lua/lua.hpp" + +RBX::Lua::LuaStubGen<525> stub525; +RBX::Lua::LuaStubGen<980> stub980; +RBX::Lua::LuaStubGen<496> stub496; +RBX::Lua::LuaStubGen<194> stub194; +RBX::Lua::LuaStubGen<608> stub608; +RBX::Lua::LuaStubGen<884> stub884; +RBX::Lua::LuaStubGen<265> stub265; +RBX::Lua::LuaStubGen<62> stub62; +RBX::Lua::LuaStubGen<336> stub336; +RBX::Lua::LuaStubGen<538> stub538; +RBX::Lua::LuaStubGen<295> stub295; +RBX::Lua::LuaStubGen<991> stub991; +RBX::Lua::LuaStubGen<795> stub795; +RBX::Lua::LuaStubGen<976> stub976; +RBX::Lua::LuaStubGen<498> stub498; +RBX::Lua::LuaStubGen<103> stub103; +RBX::Lua::LuaStubGen<723> stub723; +RBX::Lua::LuaStubGen<222> stub222; +RBX::Lua::LuaStubGen<886> stub886; +RBX::Lua::LuaStubGen<285> stub285; +RBX::Lua::LuaStubGen<927> stub927; +RBX::Lua::LuaStubGen<765> stub765; +RBX::Lua::LuaStubGen<147> stub147; +RBX::Lua::LuaStubGen<867> stub867; +RBX::Lua::LuaStubGen<597> stub597; +RBX::Lua::LuaStubGen<136> stub136; +RBX::Lua::LuaStubGen<895> stub895; +RBX::Lua::LuaStubGen<837> stub837; +RBX::Lua::LuaStubGen<631> stub631; +RBX::Lua::LuaStubGen<825> stub825; +RBX::Lua::LuaStubGen<575> stub575; +RBX::Lua::LuaStubGen<329> stub329; +RBX::Lua::LuaStubGen<435> stub435; +RBX::Lua::LuaStubGen<769> stub769; +RBX::Lua::LuaStubGen<494> stub494; +RBX::Lua::LuaStubGen<45> stub45; +RBX::Lua::LuaStubGen<31> stub31; +RBX::Lua::LuaStubGen<179> stub179; +RBX::Lua::LuaStubGen<255> stub255; +RBX::Lua::LuaStubGen<943> stub943; +RBX::Lua::LuaStubGen<810> stub810; +RBX::Lua::LuaStubGen<617> stub617; +RBX::Lua::LuaStubGen<988> stub988; +RBX::Lua::LuaStubGen<446> stub446; +RBX::Lua::LuaStubGen<213> stub213; +RBX::Lua::LuaStubGen<567> stub567; +RBX::Lua::LuaStubGen<714> stub714; +RBX::Lua::LuaStubGen<379> stub379; +RBX::Lua::LuaStubGen<301> stub301; +RBX::Lua::LuaStubGen<350> stub350; +RBX::Lua::LuaStubGen<691> stub691; +RBX::Lua::LuaStubGen<551> stub551; +RBX::Lua::LuaStubGen<24> stub24; +RBX::Lua::LuaStubGen<948> stub948; +RBX::Lua::LuaStubGen<931> stub931; +RBX::Lua::LuaStubGen<704> stub704; +RBX::Lua::LuaStubGen<343> stub343; +RBX::Lua::LuaStubGen<133> stub133; +RBX::Lua::LuaStubGen<923> stub923; +RBX::Lua::LuaStubGen<968> stub968; +RBX::Lua::LuaStubGen<735> stub735; +RBX::Lua::LuaStubGen<436> stub436; +RBX::Lua::LuaStubGen<218> stub218; +RBX::Lua::LuaStubGen<957> stub957; +RBX::Lua::LuaStubGen<364> stub364; +RBX::Lua::LuaStubGen<601> stub601; +RBX::Lua::LuaStubGen<982> stub982; +RBX::Lua::LuaStubGen<409> stub409; +RBX::Lua::LuaStubGen<899> stub899; +RBX::Lua::LuaStubGen<953> stub953; +RBX::Lua::LuaStubGen<637> stub637; +RBX::Lua::LuaStubGen<16> stub16; +RBX::Lua::LuaStubGen<402> stub402; +RBX::Lua::LuaStubGen<780> stub780; +RBX::Lua::LuaStubGen<689> stub689; +RBX::Lua::LuaStubGen<373> stub373; +RBX::Lua::LuaStubGen<273> stub273; +RBX::Lua::LuaStubGen<636> stub636; +RBX::Lua::LuaStubGen<210> stub210; +RBX::Lua::LuaStubGen<548> stub548; +RBX::Lua::LuaStubGen<804> stub804; +RBX::Lua::LuaStubGen<1> stub1; +RBX::Lua::LuaStubGen<663> stub663; +RBX::Lua::LuaStubGen<785> stub785; +RBX::Lua::LuaStubGen<811> stub811; +RBX::Lua::LuaStubGen<344> stub344; +RBX::Lua::LuaStubGen<664> stub664; +RBX::Lua::LuaStubGen<831> stub831; +RBX::Lua::LuaStubGen<783> stub783; +RBX::Lua::LuaStubGen<978> stub978; +RBX::Lua::LuaStubGen<841> stub841; +RBX::Lua::LuaStubGen<22> stub22; +RBX::Lua::LuaStubGen<520> stub520; +RBX::Lua::LuaStubGen<6> stub6; +RBX::Lua::LuaStubGen<659> stub659; +RBX::Lua::LuaStubGen<230> stub230; +RBX::Lua::LuaStubGen<72> stub72; +RBX::Lua::LuaStubGen<57> stub57; +RBX::Lua::LuaStubGen<333> stub333; +RBX::Lua::LuaStubGen<80> stub80; diff --git a/App/lua/luaStubs8.h b/App/lua/luaStubs8.h new file mode 100644 index 0000000..8616eb8 --- /dev/null +++ b/App/lua/luaStubs8.h @@ -0,0 +1,103 @@ +#pragma once +#include "lua/lua.hpp" + +RBX::Lua::LuaStubGen<355> stub355; +RBX::Lua::LuaStubGen<411> stub411; +RBX::Lua::LuaStubGen<531> stub531; +RBX::Lua::LuaStubGen<281> stub281; +RBX::Lua::LuaStubGen<67> stub67; +RBX::Lua::LuaStubGen<297> stub297; +RBX::Lua::LuaStubGen<415> stub415; +RBX::Lua::LuaStubGen<954> stub954; +RBX::Lua::LuaStubGen<410> stub410; +RBX::Lua::LuaStubGen<798> stub798; +RBX::Lua::LuaStubGen<519> stub519; +RBX::Lua::LuaStubGen<493> stub493; +RBX::Lua::LuaStubGen<299> stub299; +RBX::Lua::LuaStubGen<656> stub656; +RBX::Lua::LuaStubGen<808> stub808; +RBX::Lua::LuaStubGen<906> stub906; +RBX::Lua::LuaStubGen<778> stub778; +RBX::Lua::LuaStubGen<445> stub445; +RBX::Lua::LuaStubGen<468> stub468; +RBX::Lua::LuaStubGen<363> stub363; +RBX::Lua::LuaStubGen<902> stub902; +RBX::Lua::LuaStubGen<740> stub740; +RBX::Lua::LuaStubGen<32> stub32; +RBX::Lua::LuaStubGen<278> stub278; +RBX::Lua::LuaStubGen<767> stub767; +RBX::Lua::LuaStubGen<594> stub594; +RBX::Lua::LuaStubGen<335> stub335; +RBX::Lua::LuaStubGen<356> stub356; +RBX::Lua::LuaStubGen<376> stub376; +RBX::Lua::LuaStubGen<955> stub955; +RBX::Lua::LuaStubGen<315> stub315; +RBX::Lua::LuaStubGen<915> stub915; +RBX::Lua::LuaStubGen<788> stub788; +RBX::Lua::LuaStubGen<940> stub940; +RBX::Lua::LuaStubGen<405> stub405; +RBX::Lua::LuaStubGen<442> stub442; +RBX::Lua::LuaStubGen<250> stub250; +RBX::Lua::LuaStubGen<476> stub476; +RBX::Lua::LuaStubGen<120> stub120; +RBX::Lua::LuaStubGen<528> stub528; +RBX::Lua::LuaStubGen<142> stub142; +RBX::Lua::LuaStubGen<186> stub186; +RBX::Lua::LuaStubGen<754> stub754; +RBX::Lua::LuaStubGen<989> stub989; +RBX::Lua::LuaStubGen<25> stub25; +RBX::Lua::LuaStubGen<223> stub223; +RBX::Lua::LuaStubGen<848> stub848; +RBX::Lua::LuaStubGen<815> stub815; +RBX::Lua::LuaStubGen<658> stub658; +RBX::Lua::LuaStubGen<905> stub905; +RBX::Lua::LuaStubGen<37> stub37; +RBX::Lua::LuaStubGen<338> stub338; +RBX::Lua::LuaStubGen<53> stub53; +RBX::Lua::LuaStubGen<682> stub682; +RBX::Lua::LuaStubGen<904> stub904; +RBX::Lua::LuaStubGen<862> stub862; +RBX::Lua::LuaStubGen<533> stub533; +RBX::Lua::LuaStubGen<694> stub694; +RBX::Lua::LuaStubGen<558> stub558; +RBX::Lua::LuaStubGen<100> stub100; +RBX::Lua::LuaStubGen<518> stub518; +RBX::Lua::LuaStubGen<125> stub125; +RBX::Lua::LuaStubGen<845> stub845; +RBX::Lua::LuaStubGen<503> stub503; +RBX::Lua::LuaStubGen<340> stub340; +RBX::Lua::LuaStubGen<399> stub399; +RBX::Lua::LuaStubGen<89> stub89; +RBX::Lua::LuaStubGen<146> stub146; +RBX::Lua::LuaStubGen<917> stub917; +RBX::Lua::LuaStubGen<877> stub877; +RBX::Lua::LuaStubGen<696> stub696; +RBX::Lua::LuaStubGen<279> stub279; +RBX::Lua::LuaStubGen<368> stub368; +RBX::Lua::LuaStubGen<936> stub936; +RBX::Lua::LuaStubGen<378> stub378; +RBX::Lua::LuaStubGen<958> stub958; +RBX::Lua::LuaStubGen<962> stub962; +RBX::Lua::LuaStubGen<38> stub38; +RBX::Lua::LuaStubGen<444> stub444; +RBX::Lua::LuaStubGen<257> stub257; +RBX::Lua::LuaStubGen<607> stub607; +RBX::Lua::LuaStubGen<427> stub427; +RBX::Lua::LuaStubGen<149> stub149; +RBX::Lua::LuaStubGen<855> stub855; +RBX::Lua::LuaStubGen<135> stub135; +RBX::Lua::LuaStubGen<127> stub127; +RBX::Lua::LuaStubGen<430> stub430; +RBX::Lua::LuaStubGen<511> stub511; +RBX::Lua::LuaStubGen<748> stub748; +RBX::Lua::LuaStubGen<952> stub952; +RBX::Lua::LuaStubGen<104> stub104; +RBX::Lua::LuaStubGen<650> stub650; +RBX::Lua::LuaStubGen<771> stub771; +RBX::Lua::LuaStubGen<680> stub680; +RBX::Lua::LuaStubGen<876> stub876; +RBX::Lua::LuaStubGen<849> stub849; +RBX::Lua::LuaStubGen<251> stub251; +RBX::Lua::LuaStubGen<635> stub635; +RBX::Lua::LuaStubGen<77> stub77; +RBX::Lua::LuaStubGen<141> stub141; diff --git a/App/lua/luaStubs9.h b/App/lua/luaStubs9.h new file mode 100644 index 0000000..8472d92 --- /dev/null +++ b/App/lua/luaStubs9.h @@ -0,0 +1,103 @@ +#pragma once +#include "lua/lua.hpp" + +RBX::Lua::LuaStubGen<137> stub137; +RBX::Lua::LuaStubGen<966> stub966; +RBX::Lua::LuaStubGen<894> stub894; +RBX::Lua::LuaStubGen<95> stub95; +RBX::Lua::LuaStubGen<586> stub586; +RBX::Lua::LuaStubGen<715> stub715; +RBX::Lua::LuaStubGen<657> stub657; +RBX::Lua::LuaStubGen<244> stub244; +RBX::Lua::LuaStubGen<826> stub826; +RBX::Lua::LuaStubGen<303> stub303; +RBX::Lua::LuaStubGen<964> stub964; +RBX::Lua::LuaStubGen<732> stub732; +RBX::Lua::LuaStubGen<984> stub984; +RBX::Lua::LuaStubGen<240> stub240; +RBX::Lua::LuaStubGen<342> stub342; +RBX::Lua::LuaStubGen<264> stub264; +RBX::Lua::LuaStubGen<192> stub192; +RBX::Lua::LuaStubGen<874> stub874; +RBX::Lua::LuaStubGen<563> stub563; +RBX::Lua::LuaStubGen<644> stub644; +RBX::Lua::LuaStubGen<495> stub495; +RBX::Lua::LuaStubGen<460> stub460; +RBX::Lua::LuaStubGen<583> stub583; +RBX::Lua::LuaStubGen<39> stub39; +RBX::Lua::LuaStubGen<707> stub707; +RBX::Lua::LuaStubGen<334> stub334; +RBX::Lua::LuaStubGen<8> stub8; +RBX::Lua::LuaStubGen<132> stub132; +RBX::Lua::LuaStubGen<474> stub474; +RBX::Lua::LuaStubGen<407> stub407; +RBX::Lua::LuaStubGen<75> stub75; +RBX::Lua::LuaStubGen<542> stub542; +RBX::Lua::LuaStubGen<789> stub789; +RBX::Lua::LuaStubGen<36> stub36; +RBX::Lua::LuaStubGen<174> stub174; +RBX::Lua::LuaStubGen<252> stub252; +RBX::Lua::LuaStubGen<28> stub28; +RBX::Lua::LuaStubGen<618> stub618; +RBX::Lua::LuaStubGen<205> stub205; +RBX::Lua::LuaStubGen<838> stub838; +RBX::Lua::LuaStubGen<260> stub260; +RBX::Lua::LuaStubGen<116> stub116; +RBX::Lua::LuaStubGen<888> stub888; +RBX::Lua::LuaStubGen<91> stub91; +RBX::Lua::LuaStubGen<153> stub153; +RBX::Lua::LuaStubGen<180> stub180; +RBX::Lua::LuaStubGen<247> stub247; +RBX::Lua::LuaStubGen<850> stub850; +RBX::Lua::LuaStubGen<660> stub660; +RBX::Lua::LuaStubGen<280> stub280; +RBX::Lua::LuaStubGen<942> stub942; +RBX::Lua::LuaStubGen<665> stub665; +RBX::Lua::LuaStubGen<726> stub726; +RBX::Lua::LuaStubGen<332> stub332; +RBX::Lua::LuaStubGen<820> stub820; +RBX::Lua::LuaStubGen<592> stub592; +RBX::Lua::LuaStubGen<793> stub793; +RBX::Lua::LuaStubGen<759> stub759; +RBX::Lua::LuaStubGen<898> stub898; +RBX::Lua::LuaStubGen<746> stub746; +RBX::Lua::LuaStubGen<817> stub817; +RBX::Lua::LuaStubGen<457> stub457; +RBX::Lua::LuaStubGen<449> stub449; +RBX::Lua::LuaStubGen<950> stub950; +RBX::Lua::LuaStubGen<291> stub291; +RBX::Lua::LuaStubGen<140> stub140; +RBX::Lua::LuaStubGen<389> stub389; +RBX::Lua::LuaStubGen<913> stub913; +RBX::Lua::LuaStubGen<549> stub549; +RBX::Lua::LuaStubGen<310> stub310; +RBX::Lua::LuaStubGen<990> stub990; +RBX::Lua::LuaStubGen<259> stub259; +RBX::Lua::LuaStubGen<50> stub50; +RBX::Lua::LuaStubGen<688> stub688; +RBX::Lua::LuaStubGen<52> stub52; +RBX::Lua::LuaStubGen<755> stub755; +RBX::Lua::LuaStubGen<437> stub437; +RBX::Lua::LuaStubGen<48> stub48; +RBX::Lua::LuaStubGen<662> stub662; +RBX::Lua::LuaStubGen<189> stub189; +RBX::Lua::LuaStubGen<123> stub123; +RBX::Lua::LuaStubGen<679> stub679; +RBX::Lua::LuaStubGen<0> stub0; +RBX::Lua::LuaStubGen<283> stub283; +RBX::Lua::LuaStubGen<359> stub359; +RBX::Lua::LuaStubGen<946> stub946; +RBX::Lua::LuaStubGen<670> stub670; +RBX::Lua::LuaStubGen<271> stub271; +RBX::Lua::LuaStubGen<847> stub847; +RBX::Lua::LuaStubGen<416> stub416; +RBX::Lua::LuaStubGen<903> stub903; +RBX::Lua::LuaStubGen<595> stub595; +RBX::Lua::LuaStubGen<970> stub970; +RBX::Lua::LuaStubGen<138> stub138; +RBX::Lua::LuaStubGen<54> stub54; +RBX::Lua::LuaStubGen<590> stub590; +RBX::Lua::LuaStubGen<921> stub921; +RBX::Lua::LuaStubGen<21> stub21; +RBX::Lua::LuaStubGen<171> stub171; +RBX::Lua::LuaStubGen<673> stub673; diff --git a/App/reflection/Callback.h b/App/reflection/Callback.h new file mode 100644 index 0000000..1e34c9c --- /dev/null +++ b/App/reflection/Callback.h @@ -0,0 +1,597 @@ + +#pragma once + +#include "reflection/type.h" +#include "security/securitycontext.h" +#include "reflection/member.h" +#include "reflection/Type.h" +#include +#include + +namespace RBX +{ + namespace Reflection + { + class Callback; + + // Base class that describes a Callback + class RBXBaseClass CallbackDescriptor : public MemberDescriptor + { + public: + typedef Callback ConstMember; + typedef Callback Member; + + protected: + SignatureDescriptor signature; + bool async; + + CallbackDescriptor(ClassDescriptor& classDescriptor, const char* name, Descriptor::Attributes attributes, Security::Permissions security, bool async); + public: + inline const SignatureDescriptor& getSignature() const { return signature; } + bool isAsync() const { return async; } + }; + + class SyncCallbackDescriptor : public CallbackDescriptor + { + protected: + SyncCallbackDescriptor(ClassDescriptor& classDescriptor, const char* name, Descriptor::Attributes attributes, Security::Permissions security); + public: + typedef boost::function(shared_ptr args)> GenericFunction; + + // the preferred way to set a generic function: + virtual void setGenericCallback(DescribedBase* object, shared_ptr function) const = 0; + virtual void clearCallback(DescribedBase* object) const = 0; + + // use this function if you don't have a shared_ptr: + void setGenericCallbackHelper(DescribedBase* object, const GenericFunction& function) const; + }; + + class AsyncCallbackDescriptor : public CallbackDescriptor + { + public: + typedef boost::function)> ResumeFunction; + typedef boost::function ErrorFunction; + typedef boost::function args, ResumeFunction resumeFunction, ErrorFunction errorFunction)> GenericFunction; + + // the preferred way to set a generic function: + virtual void setGenericCallback(DescribedBase* object, shared_ptr function) const = 0; + virtual void clearCallback(DescribedBase* object) const = 0; + + // use this function if you don't have a shared_ptr: + void setGenericCallbackHelper(DescribedBase* object, const GenericFunction& function) const; + + protected: + AsyncCallbackDescriptor(ClassDescriptor& classDescriptor, const char* name, Descriptor::Attributes attributes, Security::Permissions security); + + static void callGenericImpl(shared_ptr function, shared_ptr args, + AsyncCallbackDescriptor::ResumeFunction resumeFunction, AsyncCallbackDescriptor::ErrorFunction errorFunction); + + template + void setGenericCallbackImpl(DescribedBase* object, Function Class::*member, void (Class::*onChanged)(const Function&), const Value& value) const + { + Class* c = static_cast(object); + + Function oldValue = c->*member; + c->*member = value; + if (onChanged) + (c->*onChanged)(oldValue); + } + }; + + + // A light-weight convenience class that associates a CallbackDescriptor + // with a described object to create a "Callback" + class Callback + { + const CallbackDescriptor* descriptor; + DescribedBase* instance; + public: + inline Callback(const CallbackDescriptor& descriptor, DescribedBase* instance) + :descriptor(&descriptor),instance(instance) + {} + + inline Callback(const Callback& other) + :descriptor(other.descriptor),instance(other.instance) + {} + + inline Callback& operator =(const Callback& other) + { + this->descriptor = other.descriptor; + this->instance = other.instance; + return *this; + } + + inline const RBX::Name& getName() const { + return descriptor->name; + } + + inline DescribedBase* getInstance() const + { + return instance; + } + + inline const CallbackDescriptor& getDescriptor() const + { + return *descriptor; + } + }; + + + // Base class of typed CallbackDescriptors + template + class SyncCallbackDesc : public SyncCallbackDescriptor + { + protected: + typedef typename boost::function Function; + typedef typename boost::function_traits::result_type result_type; + + template + static typename boost::enable_if, void>::type + callGeneric(shared_ptr function, shared_ptr args) + { + (*function)(args); + } + + template + static typename boost::disable_if, Result>, Result>::type + convertResult(shared_ptr result) + { + // Extract the first value in the Tuple and return it as the result. Ignore other vales + if (result->values.size() == 0) + throw std::runtime_error("Callback did not return a value"); + return result->values[0].convert(); + } + + template + static typename boost::enable_if, Result>, Result>::type + convertResult(shared_ptr result) + { + return result; + } + + template + static typename boost::disable_if, Result>::type + callGeneric(shared_ptr function, shared_ptr args) + { + shared_ptr result = (*function)(args); + return convertResult(result); + } + + class RBXInterface ISetter + { + public: + virtual ~ISetter() {} + virtual void setCallback(DescribedBase* object, const Function& value) const = 0; + }; + boost::scoped_ptr setter; + + SyncCallbackDesc(ClassDescriptor& classDescriptor, const char* name, Descriptor::Attributes attributes, Security::Permissions security): + SyncCallbackDescriptor(classDescriptor, name, attributes, security) + {} + public: + void setCallback(DescribedBase* object, const Function& value) const + { + setter->setCallback(object, value); + } + void clearCallback(DescribedBase* object) const + { + setter->setCallback(object, Function()); + } + }; + + // Specialized class that implements generic bindings and sets the signature + template + class SyncCallbackDescImpl; + + template + class SyncCallbackDescImpl : public SyncCallbackDesc + { + typedef typename SyncCallbackDesc::result_type result_type; + static result_type callGeneric(shared_ptr function) + { + shared_ptr args(new Tuple()); + return SyncCallbackDesc::template callGeneric(function, args); + } + + protected: + SyncCallbackDescImpl(ClassDescriptor& classDescriptor, const char* name, Descriptor::Attributes attributes, Security::Permissions security) + :SyncCallbackDesc(classDescriptor, name, attributes, security) + { + BOOST_STATIC_ASSERT((boost::function_traits::arity == 0)); + this->signature.resultType = &Type::singleton(); + } + public: + virtual void setGenericCallback(DescribedBase* object, shared_ptr function) const + { + this->setCallback(object, boost::bind(callGeneric, function)); + } + }; + + template + class SyncCallbackDescImpl : public SyncCallbackDesc + { + typedef typename SyncCallbackDesc::result_type result_type; + static result_type callGeneric(shared_ptr function, + // TODO: Use const ref for args and bind with boost::cref? + typename boost::function_traits::arg1_type arg1) + { + shared_ptr args(new Tuple()); + args->values.push_back(arg1); + return SyncCallbackDesc::template callGeneric(function, args); + } + + protected: + SyncCallbackDescImpl(ClassDescriptor& classDescriptor, const char* name, const char* arg1name, Descriptor::Attributes attributes, Security::Permissions security) + :SyncCallbackDesc(classDescriptor, name, attributes, security) + { + BOOST_STATIC_ASSERT((boost::function_traits::arity == 1)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1name), Type::singleton::arg1_type>()); + } + public: + virtual void setGenericCallback(DescribedBase* object, shared_ptr function) const + { + this->setCallback(object, boost::bind(callGeneric, function, _1)); + } + }; + + template + class SyncCallbackDescImpl : public SyncCallbackDesc + { + typedef typename SyncCallbackDesc::result_type result_type; + static result_type callGeneric(shared_ptr function, + typename boost::function_traits::arg1_type arg1, + typename boost::function_traits::arg2_type arg2) + { + shared_ptr args(new Tuple()); + args->values.push_back(arg1); + args->values.push_back(arg2); + return SyncCallbackDesc::template callGeneric(function, args); + } + + protected: + SyncCallbackDescImpl(ClassDescriptor& classDescriptor, const char* name, const char* arg1name, const char* arg2name, Descriptor::Attributes attributes, Security::Permissions security) + :SyncCallbackDesc(classDescriptor, name, attributes, security) + { + BOOST_STATIC_ASSERT((boost::function_traits::arity == 2)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1name), Type::singleton::arg1_type>()); + this->signature.addArgument(RBX::Name::declare(arg2name), Type::singleton::arg2_type>()); + } + public: + virtual void setGenericCallback(DescribedBase* object, shared_ptr function) const + { + this->setCallback(object, boost::bind(callGeneric, function, _1, _2)); + } + }; + + template + class SyncCallbackDescImpl : public SyncCallbackDesc + { + typedef typename SyncCallbackDesc::result_type result_type; + static result_type callGeneric(shared_ptr function, + typename boost::function_traits::arg1_type arg1, + typename boost::function_traits::arg2_type arg2, + typename boost::function_traits::arg3_type arg3) + { + shared_ptr args(new Tuple()); + args->values.push_back(arg1); + args->values.push_back(arg2); + args->values.push_back(arg3); + return SyncCallbackDesc::template callGeneric(function, args); + } + + protected: + SyncCallbackDescImpl(ClassDescriptor& classDescriptor, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, Descriptor::Attributes attributes, Security::Permissions security) + :SyncCallbackDesc(classDescriptor, name, attributes, security) + { + BOOST_STATIC_ASSERT((boost::function_traits::arity == 3)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1name), Type::singleton::arg1_type>()); + this->signature.addArgument(RBX::Name::declare(arg2name), Type::singleton::arg2_type>()); + this->signature.addArgument(RBX::Name::declare(arg3name), Type::singleton::arg3_type>()); + } + public: + virtual void setGenericCallback(DescribedBase* object, shared_ptr function) const + { + this->setCallback(object, boost::bind(callGeneric, function, _1, _2, _3)); + } + }; + + template + class SyncCallbackDescImpl : public SyncCallbackDesc + { + typedef typename SyncCallbackDesc::result_type result_type; + static result_type callGeneric(shared_ptr function, + typename boost::function_traits::arg1_type arg1, + typename boost::function_traits::arg2_type arg2, + typename boost::function_traits::arg3_type arg3, + typename boost::function_traits::arg4_type arg4) + { + shared_ptr args(new Tuple()); + args->values.push_back(arg1); + args->values.push_back(arg2); + args->values.push_back(arg3); + args->values.push_back(arg4); + return SyncCallbackDesc::template callGeneric(function, args); + } + + protected: + SyncCallbackDescImpl(ClassDescriptor& classDescriptor, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, Descriptor::Attributes attributes, Security::Permissions security) + :SyncCallbackDesc(classDescriptor, name, attributes, security) + { + BOOST_STATIC_ASSERT((boost::function_traits::arity == 4)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1name), Type::singleton::arg1_type>()); + this->signature.addArgument(RBX::Name::declare(arg2name), Type::singleton::arg2_type>()); + this->signature.addArgument(RBX::Name::declare(arg3name), Type::singleton::arg3_type>()); + this->signature.addArgument(RBX::Name::declare(arg4name), Type::singleton::arg4_type>()); + } + public: + virtual void setGenericCallback(DescribedBase* object, shared_ptr function) const + { + this->setCallback(object, boost::bind(callGeneric, function, _1, _2, _3, _4)); + } + }; + + // The fully functional descriptor that binds to class members + template + class BoundCallbackDesc : public SyncCallbackDescImpl::arity> + { + typedef typename boost::function Function; + + template + class Setter : public SyncCallbackDesc::ISetter + { + typedef void (Class::*OnChanged)(); + Function Class::*member; + OnChanged onChanged; + public: + Setter(Function Class::*member, OnChanged onChanged = NULL):member(member),onChanged(onChanged) {} + + virtual void setCallback(DescribedBase* object, const Function& value) const + { + Class* c = static_cast(object); + c->*member = value; + if (onChanged) + (c->*onChanged)(); + } + }; + public: + template + BoundCallbackDesc(const char* name, Function Class::*member, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :SyncCallbackDescImpl(Class::classDescriptor(), name, attributes, security) + { + this->setter.reset(new Setter(member)); + } + + template + BoundCallbackDesc(const char* name, Function Class::*member, void (Class::*onChanged)(), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :SyncCallbackDescImpl(Class::classDescriptor(), name, attributes, security) + { + this->setter.reset(new Setter(member, onChanged)); + } + + template + BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :SyncCallbackDescImpl(Class::classDescriptor(), name, arg1name, attributes, security) + { + this->setter.reset(new Setter(member)); + } + + template + BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, void (Class::*onChanged)(), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :SyncCallbackDescImpl(Class::classDescriptor(), name, arg1name, attributes, security) + { + this->setter.reset(new Setter(member, onChanged)); + } + + template + BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :SyncCallbackDescImpl(Class::classDescriptor(), name, arg1name, arg2name, attributes, security) + { + this->setter.reset(new Setter(member)); + } + + template + BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, void (Class::*onChanged)(), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :SyncCallbackDescImpl(Class::classDescriptor(), name, arg1name, arg2name, attributes, security) + { + this->setter.reset(new Setter(member, onChanged)); + } + + template + BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, const char* arg3name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :SyncCallbackDescImpl(Class::classDescriptor(), name, arg1name, arg2name, arg3name, attributes, security) + { + this->setter.reset(new Setter(member)); + } + + template + BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, const char* arg3name, void (Class::*onChanged)(), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :SyncCallbackDescImpl(Class::classDescriptor(), name, arg1name, arg2name, arg3name, attributes, security) + { + this->setter.reset(new Setter(member, onChanged)); + } + + template + BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :SyncCallbackDescImpl(Class::classDescriptor(), name, arg1name, arg2name, arg3name, arg4name, attributes, security) + { + this->setter.reset(new Setter(member)); + } + + template + BoundCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, void (Class::*onChanged)(), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :SyncCallbackDescImpl(Class::classDescriptor(), name, arg1name, arg2name, arg3name, arg4name, attributes, security) + { + this->setter.reset(new Setter(member, onChanged)); + } + }; + + template ::arity> + class BoundAsyncCallbackDesc; + + template + class BoundAsyncCallbackDesc : public AsyncCallbackDescriptor + { + typedef boost::function Function; + + static void callGeneric(shared_ptr function, + AsyncCallbackDescriptor::ResumeFunction resumeFunction, AsyncCallbackDescriptor::ErrorFunction errorFunction) + { + shared_ptr args(new Tuple()); + callGenericImpl(function, args, resumeFunction, errorFunction); + } + + void declareSignature() + { + BOOST_STATIC_ASSERT((boost::function_traits::arity == 0)); + this->signature.resultType = &Type::singleton::result_type>(); + } + + Function Class::*member; + void (Class::*onChanged)(const Function&); + + public: + BoundAsyncCallbackDesc(const char* name, Function Class::*member, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + : AsyncCallbackDescriptor(Class::classDescriptor(), name, attributes, security) + , member(member) + , onChanged(NULL) + { + declareSignature(); + } + + BoundAsyncCallbackDesc(const char* name, Function Class::*member, void (Class::*onChanged)(const Function&), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + : AsyncCallbackDescriptor(Class::classDescriptor(), name, attributes, security) + , member(member) + , onChanged(onChanged) + { + declareSignature(); + } + + public: + virtual void setGenericCallback(DescribedBase* object, shared_ptr function) const + { + setGenericCallbackImpl(object, member, onChanged, boost::bind(callGeneric, function, _1, _2)); + } + + virtual void clearCallback(DescribedBase* object) const + { + setGenericCallbackImpl(object, member, onChanged, Function()); + } + }; + + template + class BoundAsyncCallbackDesc : public AsyncCallbackDescriptor + { + typedef typename boost::function_traits::arg1_type Arg1; + typedef boost::function Function; + + static void callGeneric(shared_ptr function, + Arg1 arg1, + AsyncCallbackDescriptor::ResumeFunction resumeFunction, AsyncCallbackDescriptor::ErrorFunction errorFunction) + { + shared_ptr args(new Tuple()); + args->values.push_back(arg1); + callGenericImpl(function, args, resumeFunction, errorFunction); + } + + void declareSignature(const char* arg1name) + { + BOOST_STATIC_ASSERT((boost::function_traits::arity == 1)); + this->signature.resultType = &Type::singleton::result_type>(); + this->signature.addArgument(RBX::Name::declare(arg1name), Type::singleton::arg1_type>()); + } + + Function Class::*member; + void (Class::*onChanged)(const Function&); + + public: + BoundAsyncCallbackDesc(const char* name, Function Class::*member, const char* arg1name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + : AsyncCallbackDescriptor(Class::classDescriptor(), name, attributes, security) + , member(member) + , onChanged(NULL) + { + declareSignature(arg1name); + } + + BoundAsyncCallbackDesc(const char* name, Function Class::*member, const char* arg1name, void (Class::*onChanged)(const Function&), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + : AsyncCallbackDescriptor(Class::classDescriptor(), name, attributes, security) + , member(member) + , onChanged(onChanged) + { + declareSignature(arg1name); + } + + public: + virtual void setGenericCallback(DescribedBase* object, shared_ptr function) const + { + setGenericCallbackImpl(object, member, onChanged, boost::bind(callGeneric, function, _1, _2, _3)); + } + + virtual void clearCallback(DescribedBase* object) const + { + setGenericCallbackImpl(object, member, onChanged, Function()); + } + }; + + template + class BoundAsyncCallbackDesc : public AsyncCallbackDescriptor + { + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef boost::function Function; + + static void callGeneric(shared_ptr function, + Arg1 arg1, + Arg2 arg2, + AsyncCallbackDescriptor::ResumeFunction resumeFunction, AsyncCallbackDescriptor::ErrorFunction errorFunction) + { + shared_ptr args(new Tuple()); + args->values.push_back(arg1); + args->values.push_back(arg2); + callGenericImpl(function, args, resumeFunction, errorFunction); + } + + void declareSignature(const char* arg1name, const char* arg2name) + { + BOOST_STATIC_ASSERT((boost::function_traits::arity == 2)); + this->signature.resultType = &Type::singleton::result_type>(); + this->signature.addArgument(RBX::Name::declare(arg1name), Type::singleton::arg1_type>()); + this->signature.addArgument(RBX::Name::declare(arg2name), Type::singleton::arg2_type>()); + } + + Function Class::*member; + void (Class::*onChanged)(const Function&); + + public: + BoundAsyncCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + : AsyncCallbackDescriptor(Class::classDescriptor(), name, attributes, security) + , member(member) + , onChanged(NULL) + { + declareSignature(arg1name, arg2name); + } + + BoundAsyncCallbackDesc(const char* name, Function Class::*member, const char* arg1name, const char* arg2name, void (Class::*onChanged)(const Function&), Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + : AsyncCallbackDescriptor(Class::classDescriptor(), name, attributes, security) + , member(member) + , onChanged(onChanged) + { + declareSignature(arg1name, arg2name); + } + + public: + virtual void setGenericCallback(DescribedBase* object, shared_ptr function) const + { + setGenericCallbackImpl(object, member, onChanged, boost::bind(callGeneric, function, _1, _2, _3, _4)); + } + + virtual void clearCallback(DescribedBase* object) const + { + setGenericCallbackImpl(object, member, onChanged, Function()); + } + }; + } +} diff --git a/App/reflection/Descriptor.h b/App/reflection/Descriptor.h new file mode 100644 index 0000000..bb09cfa --- /dev/null +++ b/App/reflection/Descriptor.h @@ -0,0 +1,75 @@ +#pragma once + +#include "util/Name.h" +#include "boost/utility.hpp" +#include + +namespace RBX +{ + namespace Reflection + { + class Descriptor : public boost::noncopyable + { + + static void checkLockedDown() + { + // If this following assertion fails then you need to put your class + // into FactoryRegistrator::FactoryRegistrator() or somewhere else. + // Failure of this test is so severe that we want to catch it in production, too. + if (lockedDown) + RBXCRASH(); + } + public: + struct Attributes + { + bool isDeprecated; + const Descriptor* preferred; // used if isDeprecated + Attributes() + :isDeprecated(false) + ,preferred(NULL) + {} + static Attributes deprecated(const Descriptor& preferred) + { + Attributes result; + result.isDeprecated = true; + result.preferred = &preferred; + return result; + } + static Attributes deprecated() + { + Attributes result; + result.isDeprecated = true; + return result; + } + + }; + + static bool lockedDown; // After the first instance of a described class is created we cannot modify the reflection database + + const RBX::Name& name; + scoped_ptr isReplicable; + scoped_ptr isOutdated; + const Attributes attributes; + + Descriptor(const char* name, Attributes attributes) + :name(RBX::Name::declare(name)) + ,attributes(attributes) + ,isReplicable(new bool(false)) + ,isOutdated(new bool(false)) + { + checkLockedDown(); + RBXASSERT(!this->name.empty()); + } + Descriptor(const RBX::Name& name, Attributes attributes) + :name(name) + ,attributes(attributes) + ,isReplicable(new bool(false)) + ,isOutdated(new bool(false)) + { + checkLockedDown(); + RBXASSERT(!this->name.empty()); + } + virtual ~Descriptor() {} + }; + } +} diff --git a/App/reflection/EnumConverter.h b/App/reflection/EnumConverter.h new file mode 100644 index 0000000..57de72e --- /dev/null +++ b/App/reflection/EnumConverter.h @@ -0,0 +1,375 @@ +#pragma once + +#include "reflection/Type.h" +#include "util/utilities.h" +#include "util/math.h" +#include +#include +#include + +namespace RBX { + + + namespace Reflection { + + class EnumDescriptor : public Type + { + public: + static std::vector< const EnumDescriptor* >::const_iterator enumsBegin(); + static std::vector< const EnumDescriptor* >::const_iterator enumsEnd(); + static size_t allEnumSize() {return allEnums().size();} + + class Item : public Descriptor + { + public: + const EnumDescriptor& owner; + const int value; // value of the enum + const size_t index; // place in ordered enum values (0<=index EnumNameTable; +#else + typedef std::map EnumNameTable; +#endif + + private: + static EnumNameTable& allEnumsNameLookup(); + static std::vector& allEnums(); + + static bool equalValue(const Item* item, int intValue) + { + return item->value == intValue; + } + + static int count; + protected: + std::vector< const Item* > allItems; + size_t enumCount; + size_t enumCountMSB; + EnumDescriptor(const char* typeName); + ~EnumDescriptor(); + public: + size_t getEnumCount() const { return enumCount; } + size_t getEnumCountMSB() const { return enumCountMSB; } + std::vector< const Item* >::const_iterator begin() const { + return allItems.begin(); + } + std::vector< const Item* >::const_iterator end() const { + return allItems.end(); + } + static const EnumDescriptor* lookupDescriptor(const RBX::Name& name) { + EnumNameTable::const_iterator iter = allEnumsNameLookup().find(&name); + if (iter!=allEnumsNameLookup().end()) + return iter->second; + else + return NULL; + } + static const EnumDescriptor* lookupDescriptor(const Type& type) { + if (type.isEnum) + return static_cast(&type); + else + return NULL; + } + + bool isValue(int intValue) const { + return std::find_if(allItems.begin(), allItems.end(), boost::bind(&equalValue, _1, intValue)) != allItems.end(); + } + virtual const Item* lookup(const char* text) const = 0; + virtual const Item* lookup(const Variant& value) const = 0; + virtual bool convertToValue(size_t index, Variant& value) const = 0; + virtual bool convertToString(size_t index, std::string& value) const = 0; + }; + + // A thread-safe singleton! + template + class Singleton : boost::noncopyable + { + static T& doGetSingleton() + { + static T s; + return s; + } + static void initSingleton() + { + doGetSingleton(); + } + public: + static T& singleton() + { + static boost::once_flag flag = BOOST_ONCE_INIT; + boost::call_once(&initSingleton, flag); + return doGetSingleton(); + }; + }; + +template class EnumRegistrar; + + template + class EnumDesc : public EnumDescriptor + { + public: + friend class Singleton >; + static const EnumDesc& singleton() + { + return Singleton >::singleton(); + } + + private: + // You must implement the following constructor for each EnumDesc that you define + EnumDesc(); + ~EnumDesc() + { + // Force linking of EnumRegistrar, which will force clients + // of this library to define EnumRegistrar::registrar in + // their startup code. + + + Reflection::EnumRegistrar::registrar.dummy(); + + std::for_each(allItems.begin(), allItems.end(), &del_fun); + } + + std::map nameToEnum; + std::map nameToEnumLegacy; + std::vector< const RBX::Name* > enumToName; // maps enum to Name (there may be gaps) + + std::vector< std::string > enumToString; // maps enum to String (there may be gaps) + std::vector< const Item* > enumToItem; // maps enum to Item (there may be gaps) + + std::vector< Enum > intToEnum; // maps legacy values to proper enum + std::vector< Enum > indexToEnum; + std::vector< size_t > enumToIndex; // maps enum to Index (there may be gaps) + + + // Used in constructor + void addPair(Enum value, const char* name, Descriptor::Attributes attributes = Descriptor::Attributes()) + { + RBXASSERT_VERY_FAST(value >= 0); + // No spaces in enums: + RBXASSERT_VERY_FAST(std::string(name).find(' ') == std::string::npos); + // No no CamelCase in enums: + RBXASSERT_VERY_FAST(!isCamel(name)); + + const Item* item = new Item(name, attributes, value, enumCount, *this); + + allItems.push_back(item); + + if (intToEnum.size()<=(size_t)value) + intToEnum.resize(value+1, (Enum)-1); + intToEnum[value] = value; + + RBXASSERT(value>=0); + + if (enumToIndex.size()<=(size_t)value) + enumToIndex.resize(value+1, -1); + enumToIndex[value] = enumCount; + indexToEnum.push_back(value); + + if (enumToName.size()<=(size_t)value) + enumToName.resize(value+1, &RBX::Name::getNullName()); + enumToName[value] = &item->name; + + if (enumToString.size()<=(size_t)value) + enumToString.resize(value+1); + enumToString[value] = name; + + if (enumToItem.size()<=(size_t)value) + enumToItem.resize(value+1); + enumToItem[value] = item; + + nameToEnum[&item->name] = value; + + enumCount++; + enumCountMSB = Math::computeMSB(enumCount); + } + void addLegacy(int oldValue, const char* name, Enum value) + { + RBXASSERT_VERY_FAST(value >= 0); + + if (intToEnum.size()<=(size_t)oldValue) + intToEnum.resize(oldValue+1, (Enum)-1); + intToEnum[oldValue] = value; + nameToEnumLegacy[&RBX::Name::declare(name)] = value; + } + void addLegacyName(const char* name, Enum value) + { + nameToEnumLegacy[&RBX::Name::declare(name)] = value; + } + public: + const RBX::Name& convertToName(const Enum& value) const + { + RBXASSERT(value>=0); + RBXASSERT(value=enumToName.size()) + return RBX::Name::getNullName(); + + return *enumToName[value]; + } + std::string convertToString(const Enum& value) const + { + RBXASSERT(value>=0); + RBXASSERT((size_t)value=enumToString.size()) + return ""; + + return enumToString[value]; + } + const Item* convertToItem(const Enum& value) const + { + RBXASSERT(value>=0); + RBXASSERT((size_t)value=enumToItem.size()) + return NULL; + + return enumToItem[value]; + } + + bool mapIntValue(int intValue, Enum& value) const + { + if (intValue < 0) + return false; + + if ((size_t)intValue >= intToEnum.size()) + return false; + + value = intToEnum[intValue]; + if ((int)value == -1) + return false; + return true; + } + + bool convertToValue(const RBX::Name& name, Enum& value) const + { + typename std::map::const_iterator iter = nameToEnum.find(&name); + if (iter!=nameToEnum.end()) { + value = iter->second; + return true; + } + + iter = nameToEnumLegacy.find(&name); + if (iter!=nameToEnumLegacy.end()) { + value = iter->second; + return true; + } + + return false; + } + /*implement*/ const Item* lookup(const char* text) const + { + Enum e; + if (convertToValue(RBX::Name::lookup(text), e)) + return convertToItem(e); + else + return NULL; + } + /*implement*/ const Item* lookup(const Variant& value) const + { + return convertToItem(value.cast()); + } + + /*implement*/ bool convertToValue(size_t index, Variant& value) const + { + Enum enumValue; + bool result = convertToValue(index, enumValue); + value = enumValue; + return result; + } + /*implement*/ bool convertToString(size_t index, std::string& stringValue) const + { + Enum enumValue; + if(convertToValue(index, enumValue)){ + stringValue = convertToString(enumValue); + return true; + } + return false; + } + + + bool convertToValue(const char* text, Enum& value) const + { + return convertToValue(RBX::Name::lookup(text), value); + } + size_t convertToIndex(Enum value) const + { + RBXASSERT(value>=0); + if ((size_t)value \ + const Type& Type::getSingleton() \ + { \ + return EnumDesc::singleton(); \ + } \ + template<> EnumRegistrar EnumRegistrar::registrar(0); \ + template<> TypeRegistrar TypeRegistrar::registrar(0); \ + }} + + // This class is intended to prevent clients of the library + // from forgetting to initialize the enum descriptor + template + class EnumRegistrar : boost::noncopyable + { + int x; + + //// GCC does not generate the registrar variable defination & fails at Link Time. Force Construct by passing in an dummy arg to ctor. That works. WEIRD huh? + EnumRegistrar(int i):x(i) + { + // This call registers the enum descriptor + // in the reflection database + EnumDesc::singleton(); + } + public: + void dummy() + { + x++; + } + + // The instantiation of this static member must be in a unit + // that is initialized in the main thread before any objects + // are created. Otherwise the reflection database + // can change at runtime, which would be a disaster + static EnumRegistrar registrar; + }; + + + } +} diff --git a/App/reflection/Event.h b/App/reflection/Event.h new file mode 100644 index 0000000..d3b1b85 --- /dev/null +++ b/App/reflection/Event.h @@ -0,0 +1,1141 @@ + +#pragma once + +#include "reflection/type.h" +#include "reflection/member.h" +#include "security/SecurityContext.h" +#include "util/standardout.h" +#include "boost/any.hpp" +#include "boost/cast.hpp" +#include "boost/bind.hpp" +#include "boost/static_assert.hpp" +#include "boost/shared_ptr.hpp" +#include + +#include "rbx/Countable.h" +#include "rbx/signal.h" +#include "reflection/type.h" + +namespace RBX +{ + class SystemAddress; + + namespace Reflection + { + /* + TODO: Review all this code + + This is an event system inspired by boost's signal library. + + It allows clients to connect slots to a signal both in a typed way, but also + using a generic argument list. This makes it possible to interface the signals + with Lua and other runtime interfaces + + + */ + typedef std::vector< Variant > EventArguments; + + + template class TGenericSlotWrapper; + + // A GenericSlot is a slot that takes "EventArguments" instead of typed function arguments + // Thus, it can slot to any signal. + // This wrapper defines an interface to such a slot + class RBXInterface GenericSlotWrapper + : boost::noncopyable + , public RBX::Diagnostics::Countable< GenericSlotWrapper > + { + // TODO: make members private/protected as much as possible + public: + virtual ~GenericSlotWrapper() {} + virtual void execute(const EventArguments& arguments) = 0; + template + void execute1(const Arg1& arg1) + { + EventArguments args(1); + args[0] = arg1; + execute(args); + } + template + void execute2(const Arg1& arg1, const Arg2& arg2) + { + EventArguments args(2); + args[0] = arg1; + args[1] = arg2; + execute(args); + } + template + void execute3(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) + { + EventArguments args(3); + args[0] = arg1; + args[1] = arg2; + args[2] = arg3; + execute(args); + } + + template + void execute4(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) + { + EventArguments args(4); + args[0] = arg1; + args[1] = arg2; + args[2] = arg3; + args[3] = arg4; + execute(args); + } + + template + void execute5(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) + { + EventArguments args(5); + args[0] = arg1; + args[1] = arg2; + args[2] = arg3; + args[3] = arg4; + args[4] = arg5; + execute(args); + } + + template + void execute6(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5, const Arg6& arg6) + { + EventArguments args(6); + args[0] = arg1; + args[1] = arg2; + args[2] = arg3; + args[3] = arg4; + args[4] = arg5; + args[5] = arg6; + execute(args); + } + + template + void execute7(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5, const Arg6& arg6, const Arg7& arg7) + { + EventArguments args(7); + args[0] = arg1; + args[1] = arg2; + args[2] = arg3; + args[3] = arg4; + args[4] = arg5; + args[5] = arg6; + args[6] = arg7; + execute(args); + } + template + static shared_ptr create(GenericSlot slot) + { + return shared_ptr(new TGenericSlotWrapper(slot)); + } + }; + + template + class TGenericSlotWrapper + : public GenericSlotWrapper + , public RBX::Diagnostics::Countable< TGenericSlotWrapper > + { + friend class GenericSlotWrapper; + public: + TGenericSlotWrapper(const GenericSlot& slot) + :slot(slot) + { + } + GenericSlot slot; + virtual void execute(const EventArguments& arguments) + { + try + { + slot(arguments); + } + catch(RBX::base_exception& e) + { + RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "Exception caught in TGenericSlotWrapper. %s", e.what()); + } + } + }; + + //Forward declare EventDescriptor + class EventDescriptor; + // The base class of any object that fires Events + class EventSource + { + public: + virtual ~EventSource() {} + + // This is used for processing of remote events + virtual void processRemoteEvent(const EventDescriptor& descriptor, const EventArguments& args, const SystemAddress& source); + + // This is used to replicate events: + virtual void raiseEventInvocation(const EventDescriptor& descriptor, const EventArguments& args, const SystemAddress* target = NULL); + + // If the event source can exist between multiple data models then we need to use submit task + // when running lua subscribers to ensure the right data model is locked. + virtual bool useSubmitTaskForLuaListeners() const { return false; } + }; + + /* + Describes a signal of a SignalSource/DescribedBase + */ + class Event; + class RBXBaseClass EventDescriptor : public MemberDescriptor + { + public: + typedef Event ConstMember; + typedef Event Member; + + protected: + SignatureDescriptor signature; + EventDescriptor(ClassDescriptor& classDescriptor, const char* name, Security::Permissions security, Attributes attributes); + + public: + virtual rbx::signals::connection connectGeneric(EventSource* source, shared_ptr wrapper) const = 0; + const SignatureDescriptor& getSignature() const { return signature; } + + virtual bool isScriptable() const { return true; } + bool isPublic() const { return isScriptable(); } + + virtual bool isBroadcast() const { return false; } + virtual void fireEvent(EventSource* source, const EventArguments& args) const = 0; + virtual void sendEvent(EventSource* source, const EventArguments& args) const + { + RBXASSERT(false); // Should only be called on RemoteEventDesc + } + + bool operator==(const EventDescriptor& other) const { + return this == &other; + } + bool operator!=(const EventDescriptor& other) const { + return this != &other; + } + virtual void disconnectAll(EventSource* source) const = 0; + }; + + template + class RBXBaseClass EventDescBase; + + template + class RBXBaseClass EventDescBase : public EventDescriptor + { + private: + SignalType EventClass::*sig; + protected: + EventDescBase(SignalType EventClass::*sig, const char* name, Security::Permissions security, Attributes attributes) + :EventDescriptor(EventClass::classDescriptor(), name, security, attributes) + ,sig(sig) + {} + + SignalType& getSignal(EventClass* obj) const + { + return (obj->*sig); + } + public: + inline rbx::signals::connection connect(EventSource* source, const boost::function& slot) const + { + if (source) + { + EventClass* e = boost::polymorphic_downcast(source); + return (e->*sig).connect(slot); + } + else + return rbx::signals::connection(); // return an empty connection + } + + inline void disconnectAll(EventSource* source) const + { + EventClass* e = boost::polymorphic_downcast(source); + (e->*sig).disconnectAll(); + } + }; + + template + class RBXBaseClass EventDescBase : public EventDescriptor + { + private: + SignalType* (EventClass::*getOrCreate)(bool); + protected: + EventDescBase(SignalType* (EventClass::*getOrCreate)(bool), const char* name, Security::Permissions security, Attributes attributes) + :EventDescriptor(EventClass::classDescriptor(), name, security, attributes) + ,getOrCreate(getOrCreate) + {} + + SignalType& getSignal(EventClass* obj) const + { + SignalType* s = (obj->*getOrCreate)(true); + RBXASSERT(s); + return *s; + } + public: + inline rbx::signals::connection connect(EventSource* source, const boost::function& slot) const + { + if (source) + { + EventClass* e = boost::polymorphic_downcast(source); + return getSignal(e).connect(slot); + } + else + return rbx::signals::connection(); // return an empty connection + } + + inline void disconnectAll(EventSource* source) const + { + EventClass* e = boost::polymorphic_downcast(source); + SignalType* s = (e->*getOrCreate)(false); + + if (s) + s->disconnectAll(); + } + }; + + template + class EventDescImpl; + + template + class EventDescImpl<0, EventClass, Signature, SignalType, SignalGetter> : public EventDescBase + { + protected: + EventDescImpl(SignalGetter sig, const char* name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDescBase(sig, name, security, attributes) + {} + public: + rbx::signals::connection connectGeneric(EventSource* source, shared_ptr wrapper) const + { + EventArguments foo2; + return this->connect(source, boost::bind(&GenericSlotWrapper::execute, wrapper, foo2)); + } + inline void fireEvent(EventSource* source, const EventArguments& args) const + { + RBX_SIGNALS_ASSERT(args.size() == 0); + EventClass* e = boost::polymorphic_downcast(source); + return this->getSignal(e)(); + } + void fireEvent(EventClass* instance) const + { + getSignal(instance)(); + } + }; + + template + class EventDescImpl<1, EventClass, Signature, SignalType, SignalGetter> : public EventDescBase + { + protected: + EventDescImpl(SignalGetter sig, const char* name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDescBase(sig, name, security, attributes) + {} + public: + rbx::signals::connection connectGeneric(EventSource* source, shared_ptr wrapper) const { + return this->connect(source, boost::bind(&GenericSlotWrapper::execute1< + typename boost::function_traits::arg1_type>, + wrapper, _1)); + } + inline void fireEvent(EventSource* source, const EventArguments& args) const + { + RBX_SIGNALS_ASSERT(args.size() == 1); + EventClass* e = boost::polymorphic_downcast(source); + this->getSignal(e)( + args[0].cast::arg1_type>() + ); + } + void fireEvent(EventClass* instance, typename boost::function_traits::arg1_type arg1) const + { + this->getSignal(instance)(arg1); + } + + }; + + template + class EventDescImpl<2, EventClass, Signature, SignalType, SignalGetter> : public EventDescBase + { + protected: + EventDescImpl(SignalGetter sig, const char* name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDescBase(sig, name, security, attributes) + {} + public: + rbx::signals::connection connectGeneric(EventSource* source, shared_ptr wrapper) const { + return this->connect(source, boost::bind(&GenericSlotWrapper::execute2< + typename boost::function_traits::arg1_type, + typename boost::function_traits::arg2_type>, + wrapper, _1, _2)); + } + inline void fireEvent(EventSource* source, const EventArguments& args) const + { + RBX_SIGNALS_ASSERT(args.size() == 2); + EventClass* e = boost::polymorphic_downcast(source); + this->getSignal(e)( + args[0].cast::arg1_type>(), + args[1].cast::arg2_type>() + ); + } + void fireEvent(EventClass* instance, typename boost::function_traits::arg1_type arg1, typename boost::function_traits::arg2_type arg2) const + { + this->getSignal(instance)(arg1,arg2); + } + + }; + + template + class EventDescImpl<3, EventClass, Signature, SignalType, SignalGetter> : public EventDescBase + { + protected: + EventDescImpl(SignalGetter sig, const char* name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDescBase(sig, name, security, attributes) + {} + public: + rbx::signals::connection connectGeneric(EventSource* source, shared_ptr wrapper) const { + return this->connect(source, boost::bind(&GenericSlotWrapper::execute3< + typename boost::function_traits::arg1_type, + typename boost::function_traits::arg2_type, + typename boost::function_traits::arg3_type>, + wrapper, _1, _2, _3)); + } + inline void fireEvent(EventSource* source, const EventArguments& args) const + { + RBX_SIGNALS_ASSERT(args.size() == 3); + EventClass* e = boost::polymorphic_downcast(source); + this->getSignal(e)( + args[0].cast::arg1_type>(), + args[1].cast::arg2_type>(), + args[2].cast::arg3_type>() + ); + } + void fireEvent(EventClass* instance, typename boost::function_traits::arg1_type arg1, typename boost::function_traits::arg2_type arg2, typename boost::function_traits::arg3_type arg3) const + { + this->getSignal(instance)(arg1,arg2,arg3); + } + + }; + + template + class EventDescImpl<4, EventClass, Signature, SignalType, SignalGetter> : public EventDescBase + { + protected: + EventDescImpl(SignalGetter sig, const char* name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDescBase(sig, name, security, attributes) + {} + public: + rbx::signals::connection connectGeneric(EventSource* source, shared_ptr wrapper) const { + return this->connect(source, boost::bind(&GenericSlotWrapper::execute4< + typename boost::function_traits::arg1_type, + typename boost::function_traits::arg2_type, + typename boost::function_traits::arg3_type, + typename boost::function_traits::arg4_type>, + wrapper, _1, _2, _3, _4)); + } + inline void fireEvent(EventSource* source, const EventArguments& args) const + { + RBX_SIGNALS_ASSERT(args.size() == 4); + EventClass* e = boost::polymorphic_downcast(source); + this->getSignal(e)( + args[0].cast::arg1_type>(), + args[1].cast::arg2_type>(), + args[2].cast::arg3_type>(), + args[3].cast::arg4_type>() + ); + } + void fireEvent(EventClass* instance, typename boost::function_traits::arg1_type arg1, typename boost::function_traits::arg2_type arg2, typename boost::function_traits::arg3_type arg3, typename boost::function_traits::arg4_type arg4) const + { + this->getSignal(instance)(arg1,arg2,arg3,arg4); + } + + }; + + template + class EventDescImpl<5, EventClass, Signature, SignalType, SignalGetter> : public EventDescBase + { + protected: + EventDescImpl(SignalGetter sig, const char* name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDescBase(sig, name, security, attributes) + {} + public: + rbx::signals::connection connectGeneric(EventSource* source, shared_ptr wrapper) const { + return this->connect(source, boost::bind(&GenericSlotWrapper::execute5< + typename boost::function_traits::arg1_type, + typename boost::function_traits::arg2_type, + typename boost::function_traits::arg3_type, + typename boost::function_traits::arg4_type, + typename boost::function_traits::arg5_type>, + wrapper, _1, _2, _3, _4, _5)); + } + inline void fireEvent(EventSource* source, const EventArguments& args) const + { + RBX_SIGNALS_ASSERT(args.size() == 5); + EventClass* e = boost::polymorphic_downcast(source); + this->getSignal(e)( + args[0].cast::arg1_type>(), + args[1].cast::arg2_type>(), + args[2].cast::arg3_type>(), + args[3].cast::arg4_type>(), + args[4].cast::arg5_type>() + ); + } + void fireEvent(EventClass* instance, typename boost::function_traits::arg1_type arg1, typename boost::function_traits::arg2_type arg2, typename boost::function_traits::arg3_type arg3, typename boost::function_traits::arg4_type arg4, typename boost::function_traits::arg5_type arg5) const + { + this->getSignal(instance)(arg1,arg2,arg3,arg4,arg5); + } + + }; + + template + class EventDescImpl<6, EventClass, Signature, SignalType, SignalGetter> : public EventDescBase + { + protected: + EventDescImpl(SignalGetter sig, const char* name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDescBase(sig, name, security, attributes) + {} + public: + rbx::signals::connection connectGeneric(EventSource* source, shared_ptr wrapper) const { + return this->connect(source, boost::bind(&GenericSlotWrapper::execute6< + typename boost::function_traits::arg1_type, + typename boost::function_traits::arg2_type, + typename boost::function_traits::arg3_type, + typename boost::function_traits::arg4_type, + typename boost::function_traits::arg5_type, + typename boost::function_traits::arg6_type>, + wrapper, _1, _2, _3, _4, _5, _6)); + } + inline void fireEvent(EventSource* source, const EventArguments& args) const + { + RBX_SIGNALS_ASSERT(args.size() == 6); + EventClass* e = boost::polymorphic_downcast(source); + this->getSignal(e)( + args[0].cast::arg1_type>(), + args[1].cast::arg2_type>(), + args[2].cast::arg3_type>(), + args[3].cast::arg4_type>(), + args[4].cast::arg5_type>(), + args[5].cast::arg6_type>() + ); + } + void fireEvent(EventClass* instance, typename boost::function_traits::arg1_type arg1, typename boost::function_traits::arg2_type arg2, typename boost::function_traits::arg3_type arg3, typename boost::function_traits::arg4_type arg4, typename boost::function_traits::arg5_type arg5, typename boost::function_traits::arg6_type arg6) const + { + this->getSignal(instance)(arg1,arg2,arg3,arg4,arg5,arg6); + } + + }; + + template + class EventDescImpl<7, EventClass, Signature, SignalType, SignalGetter> : public EventDescBase + { + protected: + EventDescImpl(SignalGetter sig, const char* name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDescBase(sig, name, security, attributes) + {} + public: + rbx::signals::connection connectGeneric(EventSource* source, shared_ptr wrapper) const { + return this->connect(source, boost::bind(&GenericSlotWrapper::execute7< + typename boost::function_traits::arg1_type, + typename boost::function_traits::arg2_type, + typename boost::function_traits::arg3_type, + typename boost::function_traits::arg4_type, + typename boost::function_traits::arg5_type, + typename boost::function_traits::arg6_type, + typename boost::function_traits::arg7_type>, + wrapper, _1, _2, _3, _4, _5, _6, _7)); + } + inline void fireEvent(EventSource* source, const EventArguments& args) const + { + RBX_SIGNALS_ASSERT(args.size() == 7); + EventClass* e = boost::polymorphic_downcast(source); + this->getSignal(e)( + args[0].cast::arg1_type>(), + args[1].cast::arg2_type>(), + args[2].cast::arg3_type>(), + args[3].cast::arg4_type>(), + args[4].cast::arg5_type>(), + args[5].cast::arg6_type>(), + args[6].cast::arg7_type>() + ); + } + void fireEvent(EventClass* instance, typename boost::function_traits::arg1_type arg1, typename boost::function_traits::arg2_type arg2, typename boost::function_traits::arg3_type arg3, typename boost::function_traits::arg4_type arg4, typename boost::function_traits::arg5_type arg5, typename boost::function_traits::arg6_type arg6, typename boost::function_traits::arg7_type arg7) const + { + this->getSignal(instance)(arg1,arg2,arg3,arg4,arg5,arg6,arg7); + } + + }; + + + // This is the final class used for defining Events. + template< + class EventClass, // The class type that fires the event + typename Signature, // The signature of the event + typename SignalType = rbx::signal, // Optional specialization of the signal type + typename SignalGetter = SignalType EventClass::* // Optional signal getter, raw member pointer by default + > + class EventDesc : public EventDescImpl::arity, EventClass, Signature, SignalType, SignalGetter> + { + public: + EventDesc(SignalGetter sig, const char* name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :EventDescImpl<0, EventClass, Signature, SignalType, SignalGetter>(sig, name, security, attributes) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 0); + } + EventDesc(SignalGetter sig, const char* name, Descriptor::Attributes attributes) + :EventDescImpl<0, EventClass, Signature, SignalType, SignalGetter>(sig, name, Security::None, attributes) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 0); + } + EventDesc(SignalGetter sig, const char* name, const char* arg1name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :EventDescImpl<1, EventClass, Signature, SignalType, SignalGetter>(sig, name, security, attributes) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 1); + SignatureDescriptor::Item arg1(&RBX::Name::declare(arg1name), &Type::singleton::arg1_type>()); + this->signature.arguments.push_back(arg1); + } + EventDesc(SignalGetter sig, const char* name, const char* arg1name, Descriptor::Attributes attributes) + :EventDescImpl<1, EventClass, Signature, SignalType, SignalGetter>(sig, name, Security::None, attributes) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 1); + SignatureDescriptor::Item arg1(&RBX::Name::declare(arg1name), &Type::singleton::arg1_type>()); + this->signature.arguments.push_back(arg1); + } + EventDesc(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :EventDescImpl<2, EventClass, Signature, SignalType, SignalGetter>(sig, name, security, attributes) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 2); + SignatureDescriptor::Item arg1(&RBX::Name::declare(arg1name), &Type::singleton::arg1_type>()); + this->signature.arguments.push_back(arg1); + SignatureDescriptor::Item arg2(&RBX::Name::declare(arg2name), &Type::singleton::arg2_type>()); + this->signature.arguments.push_back(arg2); + } + EventDesc(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :EventDescImpl<3, EventClass, Signature, SignalType, SignalGetter>(sig, name, security, attributes) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 3); + SignatureDescriptor::Item arg1(&RBX::Name::declare(arg1name), &Type::singleton::arg1_type>()); + this->signature.arguments.push_back(arg1); + SignatureDescriptor::Item arg2(&RBX::Name::declare(arg2name), &Type::singleton::arg2_type>()); + this->signature.arguments.push_back(arg2); + SignatureDescriptor::Item arg3(&RBX::Name::declare(arg3name), &Type::singleton::arg3_type>()); + this->signature.arguments.push_back(arg3); + } + EventDesc(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :EventDescImpl<4, EventClass, Signature, SignalType, SignalGetter>(sig, name, security, attributes) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 4); + SignatureDescriptor::Item arg1(&RBX::Name::declare(arg1name), &Type::singleton::arg1_type>()); + this->signature.arguments.push_back(arg1); + SignatureDescriptor::Item arg2(&RBX::Name::declare(arg2name), &Type::singleton::arg2_type>()); + this->signature.arguments.push_back(arg2); + SignatureDescriptor::Item arg3(&RBX::Name::declare(arg3name), &Type::singleton::arg3_type>()); + this->signature.arguments.push_back(arg3); + SignatureDescriptor::Item arg4(&RBX::Name::declare(arg4name), &Type::singleton::arg4_type>()); + this->signature.arguments.push_back(arg4); + } + EventDesc(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, const char* arg5name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :EventDescImpl<5, EventClass, Signature, SignalType, SignalGetter>(sig, name, security, attributes) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 5); + SignatureDescriptor::Item arg1(&RBX::Name::declare(arg1name), &Type::singleton::arg1_type>()); + this->signature.arguments.push_back(arg1); + SignatureDescriptor::Item arg2(&RBX::Name::declare(arg2name), &Type::singleton::arg2_type>()); + this->signature.arguments.push_back(arg2); + SignatureDescriptor::Item arg3(&RBX::Name::declare(arg3name), &Type::singleton::arg3_type>()); + this->signature.arguments.push_back(arg3); + SignatureDescriptor::Item arg4(&RBX::Name::declare(arg4name), &Type::singleton::arg4_type>()); + this->signature.arguments.push_back(arg4); + SignatureDescriptor::Item arg5(&RBX::Name::declare(arg5name), &Type::singleton::arg5_type>()); + this->signature.arguments.push_back(arg5); + } + EventDesc(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, const char* arg5name, const char* arg6name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :EventDescImpl<6, EventClass, Signature, SignalType, SignalGetter>(sig, name, security, attributes) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 6); + SignatureDescriptor::Item arg1(&RBX::Name::declare(arg1name), &Type::singleton::arg1_type>()); + this->signature.arguments.push_back(arg1); + SignatureDescriptor::Item arg2(&RBX::Name::declare(arg2name), &Type::singleton::arg2_type>()); + this->signature.arguments.push_back(arg2); + SignatureDescriptor::Item arg3(&RBX::Name::declare(arg3name), &Type::singleton::arg3_type>()); + this->signature.arguments.push_back(arg3); + SignatureDescriptor::Item arg4(&RBX::Name::declare(arg4name), &Type::singleton::arg4_type>()); + this->signature.arguments.push_back(arg4); + SignatureDescriptor::Item arg5(&RBX::Name::declare(arg5name), &Type::singleton::arg5_type>()); + this->signature.arguments.push_back(arg5); + SignatureDescriptor::Item arg6(&RBX::Name::declare(arg6name), &Type::singleton::arg6_type>()); + this->signature.arguments.push_back(arg6); + } + EventDesc(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, const char* arg5name, const char* arg6name, const char* arg7name, Security::Permissions security = Security::None, Descriptor::Attributes attributes = Descriptor::Attributes()) + :EventDescImpl<7, EventClass, Signature, SignalType, SignalGetter>(sig, name, security, attributes) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 7); + SignatureDescriptor::Item arg1(&RBX::Name::declare(arg1name), &Type::singleton::arg1_type>()); + this->signature.arguments.push_back(arg1); + SignatureDescriptor::Item arg2(&RBX::Name::declare(arg2name), &Type::singleton::arg2_type>()); + this->signature.arguments.push_back(arg2); + SignatureDescriptor::Item arg3(&RBX::Name::declare(arg3name), &Type::singleton::arg3_type>()); + this->signature.arguments.push_back(arg3); + SignatureDescriptor::Item arg4(&RBX::Name::declare(arg4name), &Type::singleton::arg4_type>()); + this->signature.arguments.push_back(arg4); + SignatureDescriptor::Item arg5(&RBX::Name::declare(arg5name), &Type::singleton::arg5_type>()); + this->signature.arguments.push_back(arg5); + SignatureDescriptor::Item arg6(&RBX::Name::declare(arg6name), &Type::singleton::arg6_type>()); + this->signature.arguments.push_back(arg6); + SignatureDescriptor::Item arg7(&RBX::Name::declare(arg7name), &Type::singleton::arg7_type>()); + this->signature.arguments.push_back(arg7); + } + }; + + // A light-weight convenience class that associates a EventDescriptor + // with a described object to create a "Event" + class Event + { + protected: + const EventDescriptor* descriptor; + shared_ptr instance; + + public: + inline Event(const EventDescriptor& descriptor, const shared_ptr& instance) + :descriptor(&descriptor),instance(instance) + {} + + inline Event(const Event& other) + :descriptor(other.descriptor),instance(other.instance) + {} + inline Event& operator =(const Event& other) + { + this->descriptor = other.descriptor; + this->instance = other.instance; + return *this; + } + + inline const RBX::Name& getName() const { + return descriptor->name; + } + + inline const EventDescriptor* getDescriptor() const { + return descriptor; + } + inline shared_ptr getInstance() const + { + return instance; + } + + bool operator==(const Event& other) const { + return this->descriptor == other.descriptor && this->instance == other.instance; + } + bool operator!=(const Event& other) const { + return !operator==(other); + } + + + }; + + + std::size_t hash_value(const Event& prop); + + // class used to hold the arguments/EventDesc of an event + class EventInvocation + { + public: + const Event event; + EventArguments args; + + EventInvocation(const Event& event, const EventArguments& args) + : event(event) + , args(args) + {} + EventInvocation(const Event& event) + : event(event) + , args() + {} + + void fireEvent() + { + event.getDescriptor()->fireEvent(event.getInstance().get(), args); + } + + void replicateEvent() + { + event.getDescriptor()->sendEvent(event.getInstance().get(), args); + } + + bool operator==(const EventInvocation& other) const { + return this->event == other.event/* && this->args == other.args*/; + } + bool operator!=(const EventInvocation& other) const { + return this->event != other.event/* || this->args == other.args*/; + } + + }; + + + template + class RemoteEventDescImpl; + + template + class RemoteEventDescImpl<0,EventClass,Signature,SignalType,SignalGetter> : public EventDesc + { + protected: + RemoteEventDescImpl(SignalGetter sig, const char* name, Security::Permissions security, Descriptor::Attributes attributes) + : EventDesc(sig, name, security, attributes) + {} + public: + void fireAndReplicateEvent(EventClass* instance) + { + this->getSignal(instance)(); + replicateEvent(instance); + } + void replicateEvent(EventSource* instance) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 0); + EventArguments args(0); + + instance->raiseEventInvocation(*this, args); + } + }; + + template + class RemoteEventDescImpl<1,EventClass,Signature,SignalType,SignalGetter> : public EventDesc + { + protected: + RemoteEventDescImpl(SignalGetter sig, const char* name, const char* arg1name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDesc(sig, name, arg1name, security, attributes) + {} + public: + void fireAndReplicateEvent(EventClass* instance, typename boost::function_traits::arg1_type arg1) + { + this->fireEvent(instance, arg1); + replicateEvent(instance, arg1); + } + + void replicateEvent(EventSource* instance, typename boost::function_traits::arg1_type arg1) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 1); + EventArguments args(1); + args[0] = arg1; + + instance->raiseEventInvocation(*this, args); + } + + }; + + template + class RemoteEventDescImpl<2,EventClass,Signature,SignalType,SignalGetter> : public EventDesc + { + protected: + RemoteEventDescImpl(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDesc(sig, name, arg1name, arg2name, security, attributes) + {} + public: + void fireAndReplicateEvent(EventClass* instance, typename boost::function_traits::arg1_type arg1, typename boost::function_traits::arg2_type arg2) + { + this->fireEvent(instance, arg1,arg2); + replicateEvent(instance, arg1, arg2); + } + + void replicateEvent(EventSource* instance, typename boost::function_traits::arg1_type arg1, typename boost::function_traits::arg2_type arg2) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 2); + EventArguments args(2); + args[0] = arg1; + args[1] = arg2; + + instance->raiseEventInvocation(*this, args); + } + + }; + + template + class RemoteEventDescImpl<3,EventClass,Signature,SignalType,SignalGetter> : public EventDesc + { + protected: + RemoteEventDescImpl(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDesc(sig, name, arg1name, arg2name, arg3name, security, attributes) + {} + public: + void fireAndReplicateEvent(EventClass* instance, typename boost::function_traits::arg1_type arg1, typename boost::function_traits::arg2_type arg2, typename boost::function_traits::arg3_type arg3) + { + this->fireEvent(instance, arg1,arg2,arg3); + replicateEvent(instance, arg1, arg2, arg3); + } + + void replicateEvent(EventSource* instance, typename boost::function_traits::arg1_type arg1, typename boost::function_traits::arg2_type arg2, typename boost::function_traits::arg3_type arg3) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 3); + EventArguments args(3); + args[0] = arg1; + args[1] = arg2; + args[2] = arg3; + + instance->raiseEventInvocation(*this, args); + } + }; + + template + class RemoteEventDescImpl<4,EventClass,Signature,SignalType,SignalGetter> : public EventDesc + { + protected: + RemoteEventDescImpl(SignalType EventClass::*sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDesc(sig, name, arg1name, arg2name, arg3name, arg4name, security, attributes) + {} + public: + void fireAndReplicateEvent(EventClass* instance, + typename boost::function_traits::arg1_type arg1, + typename boost::function_traits::arg2_type arg2, + typename boost::function_traits::arg3_type arg3, + typename boost::function_traits::arg4_type arg4) + { + this->fireEvent(instance, arg1,arg2,arg3,arg4); + replicateEvent(instance, arg1, arg2, arg3, arg4); + } + + void replicateEvent(EventSource* instance, + typename boost::function_traits::arg1_type arg1, + typename boost::function_traits::arg2_type arg2, + typename boost::function_traits::arg3_type arg3, + typename boost::function_traits::arg4_type arg4) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 4); + EventArguments args(4); + args[0] = arg1; + args[1] = arg2; + args[2] = arg3; + args[3] = arg4; + + instance->raiseEventInvocation(*this, args); + } + }; + + template + class RemoteEventDescImpl<5,EventClass,Signature,SignalType,SignalGetter> : public EventDesc + { + protected: + RemoteEventDescImpl(SignalType EventClass::*sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, const char* arg5name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDesc(sig, name, arg1name, arg2name, arg3name, arg4name, arg5name, security, attributes) + {} + public: + void fireAndReplicateEvent(EventClass* instance, + typename boost::function_traits::arg1_type arg1, + typename boost::function_traits::arg2_type arg2, + typename boost::function_traits::arg3_type arg3, + typename boost::function_traits::arg4_type arg4, + typename boost::function_traits::arg5_type arg5) + { + this->fireEvent(instance, arg1,arg2,arg3,arg4,arg5); + replicateEvent(instance, arg1, arg2, arg3, arg4, arg5); + } + + void replicateEvent(EventSource* instance, + typename boost::function_traits::arg1_type arg1, + typename boost::function_traits::arg2_type arg2, + typename boost::function_traits::arg3_type arg3, + typename boost::function_traits::arg4_type arg4, + typename boost::function_traits::arg5_type arg5) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 5); + EventArguments args(5); + args[0] = arg1; + args[1] = arg2; + args[2] = arg3; + args[3] = arg4; + args[4] = arg5; + + instance->raiseEventInvocation(*this, args); + } + }; + + template + class RemoteEventDescImpl<6,EventClass,Signature,SignalType,SignalGetter> : public EventDesc + { + protected: + RemoteEventDescImpl(SignalType EventClass::*sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, const char* arg5name, const char* arg6name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDesc(sig, name, arg1name, arg2name, arg3name, arg4name, arg5name, arg6name, security, attributes) + {} + public: + void fireAndReplicateEvent(EventClass* instance, + typename boost::function_traits::arg1_type arg1, + typename boost::function_traits::arg2_type arg2, + typename boost::function_traits::arg3_type arg3, + typename boost::function_traits::arg4_type arg4, + typename boost::function_traits::arg5_type arg5, + typename boost::function_traits::arg6_type arg6) + { + this->fireEvent(instance, arg1,arg2,arg3,arg4,arg5,arg6); + replicateEvent(instance, arg1, arg2, arg3, arg4, arg5, arg6); + } + + void replicateEvent(EventSource* instance, + typename boost::function_traits::arg1_type arg1, + typename boost::function_traits::arg2_type arg2, + typename boost::function_traits::arg3_type arg3, + typename boost::function_traits::arg4_type arg4, + typename boost::function_traits::arg5_type arg5, + typename boost::function_traits::arg6_type arg6) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 6); + EventArguments args(6); + args[0] = arg1; + args[1] = arg2; + args[2] = arg3; + args[3] = arg4; + args[4] = arg5; + args[5] = arg6; + + instance->raiseEventInvocation(*this, args); + } + }; + + template + class RemoteEventDescImpl<7,EventClass,Signature,SignalType,SignalGetter> : public EventDesc + { + protected: + RemoteEventDescImpl(SignalType EventClass::*sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, const char* arg5name, const char* arg6name, const char* arg7name, Security::Permissions security, Descriptor::Attributes attributes) + :EventDesc(sig, name, arg1name, arg2name, arg3name, arg4name, arg5name, arg6name, arg7name, security, attributes) + {} + public: + void fireAndReplicateEvent(EventClass* instance, + typename boost::function_traits::arg1_type arg1, + typename boost::function_traits::arg2_type arg2, + typename boost::function_traits::arg3_type arg3, + typename boost::function_traits::arg4_type arg4, + typename boost::function_traits::arg5_type arg5, + typename boost::function_traits::arg6_type arg6, + typename boost::function_traits::arg7_type arg7) + { + this->fireEvent(instance, arg1,arg2,arg3,arg4,arg5,arg6,arg7); + replicateEvent(instance, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + } + + void replicateEvent(EventSource* instance, + typename boost::function_traits::arg1_type arg1, + typename boost::function_traits::arg2_type arg2, + typename boost::function_traits::arg3_type arg3, + typename boost::function_traits::arg4_type arg4, + typename boost::function_traits::arg5_type arg5, + typename boost::function_traits::arg6_type arg6, + typename boost::function_traits::arg7_type arg7) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 7); + EventArguments args(7); + args[0] = arg1; + args[1] = arg2; + args[2] = arg3; + args[3] = arg4; + args[4] = arg5; + args[5] = arg6; + args[6] = arg7; + + instance->raiseEventInvocation(*this, args); + } + }; + + + class RemoteEventCommon + { + public: + typedef enum { + SCRIPTING = 1, + REPLICATE_ONLY = 0 + } Functionality; + + struct Attributes : public Descriptor::Attributes + { + Functionality flags; + + Attributes(Functionality flags):flags(flags) {} + static Attributes deprecated(Functionality flags, const MemberDescriptor* preferred); + }; + typedef enum { + CLIENT_SERVER = 0, // Used to communicate between clients and servers + BROADCAST = 1 // A full broadcasts, all clients will recieve the message + } Behavior; + }; + + template< + class EventClass, // The class type that fires the event + typename Signature, // The signature of the event + typename SignalType = rbx::remote_signal, // Optional specialization of the signal type + typename SignalGetter = SignalType EventClass::* // Optional signal getter, raw member pointer by default + > + class RemoteEventDesc : public RemoteEventDescImpl::arity, EventClass, Signature, SignalType, SignalGetter> + , public RemoteEventCommon + { + protected: + const RemoteEventCommon::Behavior behavior; + const RemoteEventCommon::Functionality flags; + public: + RemoteEventDesc(SignalGetter sig, const char* name, Security::Permissions security, RemoteEventCommon::Attributes attributes, RemoteEventCommon::Behavior behavior) + :RemoteEventDescImpl<0,EventClass,Signature,SignalType,SignalGetter>(sig, name, security, attributes) + ,flags(attributes.flags) + ,behavior(behavior) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 0); + } + RemoteEventDesc(SignalGetter sig, const char* name, const char* arg1name, Security::Permissions security, RemoteEventCommon::Attributes attributes, RemoteEventCommon::Behavior behavior) + :RemoteEventDescImpl<1,EventClass,Signature,SignalType,SignalGetter>(sig, name, arg1name, security, attributes) + ,flags(attributes.flags) + ,behavior(behavior) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 1); + } + RemoteEventDesc(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, Security::Permissions security, RemoteEventCommon::Attributes attributes, RemoteEventCommon::Behavior behavior) + :RemoteEventDescImpl<2,EventClass,Signature,SignalType,SignalGetter>(sig, name, arg1name, arg2name, security, attributes) + ,flags(attributes.flags) + ,behavior(behavior) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 2); + } + RemoteEventDesc(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, Security::Permissions security, RemoteEventCommon::Attributes attributes, RemoteEventCommon::Behavior behavior) + :RemoteEventDescImpl<3,EventClass,Signature,SignalType,SignalGetter>(sig, name, arg1name, arg2name, arg3name, security, attributes) + ,flags(attributes.flags) + ,behavior(behavior) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 3); + } + RemoteEventDesc(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, Security::Permissions security, RemoteEventCommon::Attributes attributes, RemoteEventCommon::Behavior behavior) + :RemoteEventDescImpl<4,EventClass,Signature,SignalType,SignalGetter>(sig, name, arg1name, arg2name, arg3name, arg4name, security, attributes) + ,flags(attributes.flags) + ,behavior(behavior) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 4); + } + RemoteEventDesc(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name ,const char* arg5name, Security::Permissions security, RemoteEventCommon::Attributes attributes, RemoteEventCommon::Behavior behavior) + :RemoteEventDescImpl<5,EventClass,Signature,SignalType,SignalGetter>(sig, name, arg1name, arg2name, arg3name, arg4name, arg5name, security, attributes) + ,flags(attributes.flags) + ,behavior(behavior) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 5); + } + RemoteEventDesc(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, const char* arg5name, const char* arg6name, Security::Permissions security, RemoteEventCommon::Attributes attributes, RemoteEventCommon::Behavior behavior) + :RemoteEventDescImpl<6,EventClass,Signature,SignalType,SignalGetter>(sig, name, arg1name, arg2name, arg3name, arg4name, arg5name, arg6name, security, attributes) + ,flags(attributes.flags) + ,behavior(behavior) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 6); + } + RemoteEventDesc(SignalGetter sig, const char* name, const char* arg1name, const char* arg2name, const char* arg3name, const char* arg4name, const char* arg5name, const char* arg6name, const char* arg7name, Security::Permissions security, RemoteEventCommon::Attributes attributes, RemoteEventCommon::Behavior behavior) + :RemoteEventDescImpl<7,EventClass,Signature,SignalType,SignalGetter>(sig, name, arg1name, arg2name, arg3name, arg4name, arg5name, arg6name, arg7name, security, attributes) + ,flags(attributes.flags) + ,behavior(behavior) + { + BOOST_STATIC_ASSERT(boost::function_traits::arity == 7); + } + + SignalType* getSignalPtr(EventSource* source) + { + if (source) + { + EventClass* e = boost::polymorphic_downcast(source); + + return &this->getSignal(e); + } + RBXASSERT(0); + return NULL; + } + + /*override*/ bool isScriptable() const + { + return (flags & 1) != 0; + } + + /*override*/ bool isBroadcast() const + { + return (behavior & 1) != 0; + } + + /*override*/ void sendEvent(EventSource* instance, const EventArguments& args) const + { + instance->raiseEventInvocation(*this, args); + } + }; + } +} diff --git a/App/reflection/Function.h b/App/reflection/Function.h new file mode 100644 index 0000000..102946b --- /dev/null +++ b/App/reflection/Function.h @@ -0,0 +1,108 @@ + +#pragma once + +#include "reflection/type.h" +#include "security/securitycontext.h" +#include "reflection/member.h" +#include "util/G3DCore.h" +#include "util/Region3.h" +#include "util/Region3Int16.h" + +struct lua_State; + +namespace RBX +{ + namespace Reflection + { + class Function; + class EnumDescriptor; + + // Base that describes a Function + class RBXBaseClass FunctionDescriptor : public MemberDescriptor + { + public: + enum Kind + { + Kind_Default, + Kind_Custom + }; + + class RBXInterface Arguments + { + public: + Variant returnValue; + + virtual size_t size() const = 0; + // Place the value for the requested parameter in "value". + // + // index: 1-based index into the argument list + // returns: true if the index contains a valid argument + // value: the value to set. the function returns false then value is unchanged + virtual bool getVariant(int index, Variant& value) const = 0; + virtual bool getBool(int index, bool& value) const = 0; + virtual bool getLong(int index, long& value) const = 0; + virtual bool getDouble(int index, double& value) const = 0; + virtual bool getString(int index, std::string& value) const = 0; + virtual bool getVector3int16(int index, Vector3int16& value) const = 0; + virtual bool getRegion3int16(int index, Region3int16& value) const = 0; + virtual bool getVector3(int index, Vector3& value) const = 0; + virtual bool getRegion3(int index, Region3& value) const = 0; + virtual bool getRect(int index, Rect2D& value) const = 0; + virtual bool getObject(int index, shared_ptr& value) const = 0; + virtual bool getEnum(int index, const EnumDescriptor& desc, int& value) const = 0; + }; + typedef Function ConstMember; + typedef Function Member; + + protected: + SignatureDescriptor signature; + Kind kind; + FunctionDescriptor(ClassDescriptor& classDescriptor, const char* name, Security::Permissions security, Attributes attributes); + + public: + const SignatureDescriptor& getSignature() const { return signature; } + + Kind getKind() const { return kind; } + + virtual int executeCustom(DescribedBase* instance, lua_State*) const { return 0; } + + virtual void execute(DescribedBase* instance, Arguments& arguments) const = 0; + }; + + + // A light-weight convenience class that associates a FunctionDescriptor + // with a described object to create a "Function" + class Function + { + protected: + const FunctionDescriptor* descriptor; + DescribedBase* instance; + public: + inline Function(const FunctionDescriptor& descriptor, DescribedBase* instance) + :descriptor(&descriptor),instance(instance) + {} + + inline Function(const Function& other) + :descriptor(other.descriptor),instance(other.instance) + {} + inline Function& operator =(const Function& other) + { + this->descriptor = other.descriptor; + this->instance = other.instance; + return *this; + } + + inline const RBX::Name& getName() const { + return descriptor->name; + } + + inline const FunctionDescriptor* getDescriptor() const { + return descriptor; + } + + void execute(FunctionDescriptor::Arguments& arguments) const { + return descriptor->execute(const_cast(instance), arguments); + } + }; + } +} diff --git a/App/reflection/Object.h b/App/reflection/Object.h new file mode 100644 index 0000000..a832f81 --- /dev/null +++ b/App/reflection/Object.h @@ -0,0 +1,337 @@ + +#pragma once + +#include "reflection/Property.h" +#include "reflection/Function.h" +#include "reflection/YieldFunction.h" +#include "Reflection/Event.h" +#include "reflection/Callback.h" + +#include +#include "boost/crc.hpp" + +namespace RBX +{ + namespace Reflection + { + enum ReplicationLevel{ + NEVER_REPLICATE = 0, //Never replicate this object + STANDARD_REPLICATE= 1, //Replicate to/from server according to standard rules + PLAYER_REPLICATE = 2, //Replicate changes to/from the "player" who owns this object + } ; + + class ClassDescriptor + : public Descriptor + , public MemberDescriptorContainer + , public MemberDescriptorContainer + , public MemberDescriptorContainer + , public MemberDescriptorContainer + , public MemberDescriptorContainer + { + public: + typedef std::vector ClassDescriptors; + + enum Functionality{ + PERSISTENT = 0x1 + 0x2 + 0x8 + 0x10, // isPublic, Replicate, canXmlWrite, isScriptable + PERSISTENT_PLAYER = 0x1 + 0x4 + 0x8 + 0x10, // isPublic, ReplicatePlayer, canXmlWrite, isScriptable + PERSISTENT_LOCAL = 0x1 + 0x0 + 0x8 + 0x10, // isPublic, canXmlWrite, isScriptable + RUNTIME = 0x1 + 0x2 + 0x0 + 0x10, // isPublic, Replicate, isScriptable + RUNTIME_PLAYER = 0x1 + 0x4 + 0x0 + 0x10, // isPublic, ReplicatePlayer, isScriptable + RUNTIME_LOCAL = 0x1 + 0x0 + 0x0 + 0x10, // isPublic, isScriptable + INTERNAL = 0x1 + 0x2 + 0x0 + 0x0, // isPublic, Replicate + INTERNAL_PLAYER = 0x1 + 0x4 + 0x0 + 0x0, // isPublic, ReplicatePlayer, + INTERNAL_LOCAL = 0x1 + 0x0 + 0x0 + 0x0, // isPublic + PERSISTENT_HIDDEN = 0x1 + 0x2 + 0x8 + 0x0, // isPublic, Replicate, canXmlWrite + PERSISTENT_LOCAL_INTERNAL = 0x1 + 0x0 + 0x8 + 0x0, // isPublic, canXmlWrite + }; + + struct Attributes : public Descriptor::Attributes + { + Functionality flags; + + Attributes(Functionality flags):flags(flags) {} + static Attributes deprecated(Functionality flags, const ClassDescriptor* preferred); + }; + + const Security::Permissions security; + + private: + ClassDescriptor(); + static ClassDescriptors& allClasses(); + + ClassDescriptors derivedClasses; + ClassDescriptor* const base; + const unsigned bReplicateType : 2; + const unsigned bCanXmlWrite : 1; + const unsigned bIsScriptable : 1; + + static int count; + + public: + ClassDescriptor(ClassDescriptor& base, const char* name, Attributes attributes, Security::Permissions security); + ~ClassDescriptor() { count--; } + + const ClassDescriptor* getBase() const { return base; } + + bool isBaseOf(const ClassDescriptor& child) const; + bool isA(const ClassDescriptor& test) const; + + bool isBaseOf(const char* childName) const; + bool isA(const char* testName) const; + + inline ReplicationLevel getReplicationLevel() const { return (ReplicationLevel)bReplicateType; } + inline bool isScriptCreatable() const { return bIsScriptable != 0; } + inline bool isSerializable() const { return bCanXmlWrite != 0; } + + // The root ClassDescriptor of all other Descriptors + static ClassDescriptor& rootDescriptor() + { + static ClassDescriptor root; + return root; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Class enumeration + static ClassDescriptors::const_iterator all_begin() { + return allClasses().begin(); + } + static ClassDescriptors::const_iterator all_end() { + return allClasses().end(); + } + static size_t all_size() + { + return allClasses().size(); + } + static unsigned int checksum(); + static unsigned int checksum(const PropertyDescriptor* t, boost::crc_32_type& result); + static unsigned int checksum(const EventDescriptor* t, boost::crc_32_type& result); + static unsigned int checksum(const ClassDescriptor* t, boost::crc_32_type& result); + static unsigned int checksum(const Type* t, boost::crc_32_type& result); + + ///////////////////////////////////////////////////////////////////////////////// + // Derived Class enumeration + ClassDescriptors::const_iterator derivedClasses_begin() const { + return derivedClasses.begin(); + } + ClassDescriptors::const_iterator derivedClasses_end() const { + return derivedClasses.end(); + } + + ///////////////////////////////////////////////////////////////////////////////// + // Convenience functions for enumerating and querying members of a Class + PropertyDescriptor* findPropertyDescriptor(const char* name) const + { + return MemberDescriptorContainer::findDescriptor(name); + } + FunctionDescriptor* findFunctionDescriptor(const char* name) const + { + return MemberDescriptorContainer::findDescriptor(name); + } + YieldFunctionDescriptor* findYieldFunctionDescriptor(const char* name) const + { + return MemberDescriptorContainer::findDescriptor(name); + } + EventDescriptor* findEventDescriptor(const char* name) const + { + return MemberDescriptorContainer::findDescriptor(name); + } + CallbackDescriptor* findCallbackDescriptor(const char* name) const + { + return MemberDescriptorContainer::findDescriptor(name); + } + + template + typename MemberDescriptorContainer::Collection::const_iterator begin() const { + return MemberDescriptorContainer::descriptors_begin(); + } + template + typename MemberDescriptorContainer::Collection::const_iterator end() const { + return MemberDescriptorContainer::descriptors_end(); + } + + bool operator==(const ClassDescriptor& other) const; + bool operator!=(const ClassDescriptor& other) const; + }; + + // Convenience typedefs: + typedef MemberDescriptorContainer::ConstIterator ConstPropertyIterator; + typedef MemberDescriptorContainer::Iterator PropertyIterator; + typedef MemberDescriptorContainer::ConstIterator FunctionIterator; + typedef MemberDescriptorContainer::ConstIterator YieldFunctionIterator; + typedef MemberDescriptorContainer::ConstIterator ConstSignalIterator; + typedef MemberDescriptorContainer::Iterator SignalIterator; + typedef MemberDescriptorContainer::Iterator CallbackIterator; + + // The base class of any class that supports Reflection + class RBXBaseClass DescribedBase + : public EventSource + , public boost::enable_shared_from_this + { + protected: + // Each instance has a reference to it's most-specific ClassDescriptor: + const ClassDescriptor* descriptor; + boost::scoped_ptr xmlId; + + public: + // The ClassDescriptor for this base class + static ClassDescriptor& classDescriptor() + { + return ClassDescriptor::rootDescriptor(); + } + + DescribedBase() + { + Descriptor::lockedDown = true; // See Descriptor::checkLockedDown() for an explanation + + // By default, each DescribedBase has a null ClassDescriptor + this->descriptor = &classDescriptor(); + } + + virtual ~DescribedBase() + { + } + + inline const ClassDescriptor& getDescriptor() const { return *descriptor; }; + + template + inline bool isA() const + { + return getDescriptor().isA(T::classDescriptor()); + } + + template + static inline bool isA(const DescribedBase* instance) + { + return instance ? instance->getDescriptor().isA(T::classDescriptor()) : false; + } + + // This function is slower than the others + bool isA(std::string className) + { + return getDescriptor().isA(className.c_str()); + } + + // Regular dynamic_casts are very slow. These faster versions uses our reflection framework to determine type then uses static_cast for speed. + // Use these functions to replace dynamic_casts for classes that derives from DescribedCreatable or DescribedNonCreatable. + template + inline T* fastDynamicCast() + { + return (getDescriptor().isA(T::classDescriptor())) ? static_cast(this) : NULL; + } + + template + inline const T* fastDynamicCast() const + { + return (getDescriptor().isA(T::classDescriptor())) ? static_cast(this) : NULL; + } + + template + static inline T* fastDynamicCast(DescribedBase* instance) + { + return (instance && instance->getDescriptor().isA(T::classDescriptor())) ? static_cast(instance) : NULL; + } + + template + static inline const T* fastDynamicCast(const DescribedBase* instance) + { + return (instance && instance->getDescriptor().isA(T::classDescriptor())) ? static_cast(instance) : NULL; + } + + // This function replaces shared_dynamic_cast for classes that derives from DescribedCreatable or DescribedNonCreatable. + template + static inline shared_ptr fastSharedDynamicCast(const shared_ptr& instance) + { + return isA(instance.get()) ? shared_static_cast(instance) : shared_ptr(); + } + + + ///////////////////////////////////////////////////////////////////////////////// + // Convenience functions for getting members of a described object + PropertyDescriptor* findPropertyDescriptor(const char* name) + { + return getDescriptor().MemberDescriptorContainer::findDescriptor(name); + } + ConstPropertyIterator properties_begin() const { + return getDescriptor().MemberDescriptorContainer::members_begin(this); + } + ConstPropertyIterator properties_end() const { + return getDescriptor().MemberDescriptorContainer::members_end(this); + } + PropertyIterator properties_begin() { + return getDescriptor().MemberDescriptorContainer::members_begin(this); + } + PropertyIterator properties_end() { + return getDescriptor().MemberDescriptorContainer::members_end(this); + } + + FunctionDescriptor* findFunctionDescriptor(const char* name) + { + return getDescriptor().MemberDescriptorContainer::findDescriptor(name); + } + FunctionIterator functions_begin() const { + return getDescriptor().MemberDescriptorContainer::members_begin(this); + } + FunctionIterator functions_end() const { + return getDescriptor().MemberDescriptorContainer::members_end(this); + } + + YieldFunctionDescriptor* findYieldFunctionDescriptor(const char* name) const + { + return getDescriptor().MemberDescriptorContainer::findDescriptor(name); + } + + YieldFunctionIterator yield_functions_begin() const { + return getDescriptor().MemberDescriptorContainer::members_begin(this); + } + YieldFunctionIterator yield_functions_end() const { + return getDescriptor().MemberDescriptorContainer::members_end(this); + } + + CallbackDescriptor* findCallbackDescriptor(const char* name) + { + return getDescriptor().MemberDescriptorContainer::findDescriptor(name); + } + CallbackIterator callbacks_begin() { + return getDescriptor().MemberDescriptorContainer::members_begin(this); + } + CallbackIterator callbacks_end() { + return getDescriptor().MemberDescriptorContainer::members_end(this); + } + + EventDescriptor* findSignalDescriptor(const char* name) const + { + return getDescriptor().MemberDescriptorContainer::findDescriptor(name); + } + + ConstSignalIterator signals_begin() const { + return getDescriptor().MemberDescriptorContainer::members_begin(this); + } + ConstSignalIterator signals_end() const { + return getDescriptor().MemberDescriptorContainer::members_end(this); + } + SignalIterator signals_begin() { + return getDescriptor().MemberDescriptorContainer::members_begin(this); + } + SignalIterator signals_end() { + return getDescriptor().MemberDescriptorContainer::members_end(this); + } + + const std::string* getXmlId() const { + return xmlId.get(); + } + + void setXmlId(const std::string& newId) { + if (!xmlId) + { + xmlId.reset(new std::string(newId)); + } + else + { + *xmlId = newId; + } + } + + virtual const RBX::Name& getClassName() const = 0; + }; + } +} diff --git a/App/reflection/Property.h b/App/reflection/Property.h new file mode 100644 index 0000000..9b3874e --- /dev/null +++ b/App/reflection/Property.h @@ -0,0 +1,491 @@ + +#pragma once + +#include "reflection/member.h" +#include "reflection/enumconverter.h" +#include "reflection/type.h" +#include "v8xml/xmlelement.h" +#include "V8Xml/Reference.h" // TODO: Reflection namespace should not know about V8Tree +#include "boost/cast.hpp" + +namespace RBX +{ + namespace Reflection + { + class ConstProperty; + class Property; + + typedef enum { + READONLY, + READWRITE + } Mutability; + + // Base that describes a Property + class RBXBaseClass PropertyDescriptor : public MemberDescriptor + { + private: + unsigned bIsPublic : 1; + unsigned bIsEditable : 1; + unsigned bCanReplicate : 1; + unsigned bCanXmlRead : 1; + unsigned bCanXmlWrite : 1; + unsigned bIsScriptable : 1; + unsigned bAlwaysClone : 1; + + public: + typedef ConstProperty ConstMember; + typedef Property Member; + + public: + // Note: isPublic == PropertyUI shown, and Can BaseScript against. ToDO: possibly split? + + enum Functionality { + STANDARD = 1 + 2 + 4 + 8 + 16, // isPublic, canReplicate, canXmlRead , canXmlWrite, isScriptable + NO_XML_WRITE = 1 + 2 + 4 + 0 + 16, // isPublic, canReplicate, canXmlRead , , isScriptable + UI = 1 + 0 +(4)+ 0 + 16, // isPublic, (canXmlRead), isScriptable //Remove canXmlRead from UI + SCRIPTING = 1 + 2 + 0 + 0 + 16, // isPublic, canReplicate, isScriptable + STREAMING = 0 + 2 + 4 + 8 + 0, // canReplicate, canXmlRead , canXmlWrite, + CLUSTER = 0 + 0 + 4 + 8 + 0, // canXmlRead , canXmlWrite, + LEGACY = 0 + 0 + 4 + 0 + 0, // canXmlRead , + REPLICATE_ONLY = 0 + 2 + 0 + 0 + 0, // canReplicate + LEGACY_SCRIPTING = 0 + 0 + 4 + 0 + 16, // canXmlRead , isScriptable + HIDDEN_SCRIPTING = 0 + 0 + 0 + 0 + 16, // isScriptable + PUBLIC_SERIALIZED = 1 + 0 + 4 + 8 + 0, // isPublic, canXmlRead , canXmlWrite, + REPLICATE_CLONE = 0 + 2 + 0 + 0 + 0 + 32, // canReplicate alwaysClone + STANDARD_NO_REPLICATE = 1 + 0 + 4 + 8 + 16, // isPublic, canXmlRead , canXmlWrite, isScriptable + STANDARD_NO_SCRIPTING = 1 + 2 + 4 + 8 + 0, // isPublic, canReplicate, canXmlRead , canXmlWrite + PUBLIC_REPLICATE = 1 + 2 + 0 + 0 + 0, // isPublic, canReplicate + }; + + struct Attributes : public Descriptor::Attributes + { + Functionality flags; + + Attributes():flags(STANDARD) {} + Attributes(Functionality flags):flags(flags) {} + static Attributes deprecated(const MemberDescriptor& preferred, Functionality flags = UI); + static Attributes deprecated(Functionality flags = UI); + }; + + const Type& type; + const bool bIsEnum; + + protected: + PropertyDescriptor(ClassDescriptor& classDescriptor, const Type& type, const char* name, const char* category, Attributes attributes, Security::Permissions security, bool isEnum = false); + + inline void checkFlags() + { + if (isWriteOnly()) + { + bCanXmlWrite = 0; + bCanReplicate = 0; + } + if (isReadOnly()) + { + bCanXmlRead = 0; + bCanReplicate = 0; + } + } + public: + inline bool isPublic() const { return bIsPublic != 0; } + inline bool isScriptable() const { return bIsScriptable != 0; } + + void setEditable(bool editable) { bIsEditable = editable ? 1 : 0; } + inline bool isEditable() const { return bIsEditable != 0; } + + virtual bool isReadOnly() const = 0; + virtual bool isWriteOnly() const = 0; + inline bool canXmlRead() const + { + RBXASSERT(bCanXmlRead == 0 || !isReadOnly()); + return bCanXmlRead != 0; + } + inline bool canXmlWrite() const + { + RBXASSERT(bCanXmlWrite == 0 || !isWriteOnly()); + return bCanXmlWrite != 0; + } + inline bool canReplicate() const + { + RBXASSERT(bCanReplicate == 0 || (!isReadOnly() && !isWriteOnly())); + return bCanReplicate != 0; + } + inline bool alwaysClone() const + { + return bAlwaysClone != 0; + } + + bool operator==(const PropertyDescriptor& other) const { + return this == &other; + } + bool operator!=(const PropertyDescriptor& other) const { + return this != &other; + } + + virtual bool equalValues(const DescribedBase* a, const DescribedBase* b) const = 0; + + virtual void getVariant(const DescribedBase* instance, Variant& value) const = 0; + virtual void setVariant(DescribedBase* instance, const Variant& value) const = 0; + virtual void copyValue(const DescribedBase* source, DescribedBase* destination) const = 0; + + virtual int getDataSize(const DescribedBase* instance) const = 0; + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // String conversion interface + public: + virtual bool hasStringValue() const = 0; + virtual std::string getStringValue(const DescribedBase* instance) const { + if (hasStringValue()) + debugAssertM(false, "you must implement getStringValue"); + else + debugAssertM(false, "don't call getStringValue when hasStringValue()==false"); + return ""; + } + virtual bool setStringValue(DescribedBase* instance, const std::string& text) const { + if (hasStringValue()) + debugAssertM(false, "you must implement setStringValue"); + else + debugAssertM(false, "don't call setStringValue when hasStringValue()==false"); + return false; + } + //\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/ + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // Xml streaming interface + public: + XmlElement* write(const DescribedBase* instance, bool ignoreWriteProtection = false) const; + virtual void read(DescribedBase* instance, const XmlElement* element, RBX::IReferenceBinder& binder) const; + private: + virtual void writeValue(const DescribedBase* instance, XmlElement* element) const = 0; + virtual void readValue(DescribedBase* instance, const XmlElement* element, RBX::IReferenceBinder& binder) const = 0; + //\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/ + + }; + + + template + class TypedPropertyDescriptor : public PropertyDescriptor + { + private: + typedef PropertyDescriptor Super; + + public: + class RBXInterface GetSet + { + public: + virtual bool isReadOnly() const = 0; + virtual bool isWriteOnly() const = 0; + virtual V getValue(const DescribedBase* object) const = 0; + virtual void setValue(DescribedBase* object, const V& value) const = 0; + }; + protected: + std::auto_ptr getset; + TypedPropertyDescriptor(ClassDescriptor& classDescriptor, const Type& type, const char* name, const char* category, std::auto_ptr getset, Attributes flags, Security::Permissions security) + :PropertyDescriptor(classDescriptor, type, name, category, flags, security),getset(getset) + { + if (this->getset.get()) + this->checkFlags(); + } + TypedPropertyDescriptor(ClassDescriptor& classDescriptor, const char* name, const char* category, std::auto_ptr getset, Attributes flags, Security::Permissions security) + :PropertyDescriptor(classDescriptor, Type::singleton(), name, category, flags, security),getset(getset) + { + if (this->getset.get()) + this->checkFlags(); + } + public: + /*implement*/ V get(const DescribedBase* instance) const + { + return getset->getValue(instance); + } + /*implement*/ void set(DescribedBase* instance, const V& value) const + { + getset->setValue(instance, value); + } + + /*implement*/ void getVariant(const DescribedBase* instance, Variant& value) const + { + value = getset->getValue(instance); + } + /*implement*/ void setVariant(DescribedBase* instance, const Variant& value) const + { + // TODO: This might be inefficient. How is the value stored in getset??? + getset->setValue(instance, value.get()); + } + /*implement*/ void copyValue(const DescribedBase* source, DescribedBase* destination) const + { + set(destination, get(source)); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // Variant interface + public: + virtual bool isReadOnly() const { + return getset->isReadOnly(); + } + + virtual bool isWriteOnly() const { + return getset->isWriteOnly(); + } + + V getValue(const DescribedBase* object) const { + return getset->getValue(object); + } + + void setValue(DescribedBase* object, const V& value) const { + getset->setValue(object, value); + } + + /*implement*/ bool equalValues(const DescribedBase* a, const DescribedBase* b) const { + return getValue(a) == getValue(b); + } + + virtual int getDataSize(const DescribedBase* instance) const; + + //\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/ + + virtual bool hasStringValue() const; + virtual std::string getStringValue(const DescribedBase* instance) const; + virtual bool setStringValue(DescribedBase* instance, const std::string& text) const; + private: + virtual void readValue(DescribedBase* instance, const XmlElement* element, IReferenceBinder& binder) const; + virtual void writeValue(const DescribedBase* instance, XmlElement* element) const; + }; + + // A light-weight convenience class that associates a PropertyDescriptor + // with a described object to create a "Property" + class ConstProperty + { + protected: + const PropertyDescriptor* descriptor; + const DescribedBase* instance; + public: + inline ConstProperty():descriptor(0),instance(0) {} + inline ConstProperty(const PropertyDescriptor& descriptor, const DescribedBase* instance) + :descriptor(&descriptor),instance(instance) + { + RBXASSERT(!instance || descriptor.isMemberOf(instance)); + } + + inline ConstProperty(const ConstProperty& other) + :descriptor(other.descriptor),instance(other.instance) + {} + + inline const DescribedBase* getInstance() const { return instance; } + inline const PropertyDescriptor& getDescriptor() const { return *descriptor; } + + inline ConstProperty& operator =(const ConstProperty& other) + { + this->descriptor = other.descriptor; + this->instance = other.instance; + return *this; + } + + inline bool operator ==(const ConstProperty& other) const + { + return (this->descriptor == other.descriptor) && (this->instance == other.instance); + } + + inline const RBX::Name& getName() const { + return descriptor->name; + } + + template + inline bool isValueType() const + { + return descriptor->type==Type::singleton(); + } + template + inline V getValue() const + { + RBXASSERT(isValueType()); + return static_cast*>(descriptor)->getValue(instance); + } + + inline bool hasStringValue() const + { + return descriptor->hasStringValue(); + } + inline std::string getStringValue() const + { + return descriptor->getStringValue(instance); + } + + inline XmlElement* write() const + { + return descriptor->write(instance); + } + }; + + + // A light-weight convenience class that associates a PropertyDescriptor + // with a described object to create a "Property" + class Property : public ConstProperty + { + public: + inline Property(const PropertyDescriptor& descriptor, DescribedBase* instance) + :ConstProperty(descriptor, instance) + {} + inline Property(const Property& other) + :ConstProperty(*other.descriptor, other.instance) + {} + inline Property& operator =(const Property& other) + { + this->descriptor = other.descriptor; + this->instance = other.instance; + return *this; + } + inline bool operator ==(const Property& other) const + { + return this->descriptor==other.descriptor && this->instance==other.instance; + } + + inline bool operator !=(const Property& other) const + { + return this->descriptor!=other.descriptor || this->instance!=other.instance; + } + + DescribedBase* getInstance() const { return const_cast(instance); } + + template + inline void setValue(const V& value) + { + RBXASSERT(isValueType()); + static_cast*>(descriptor)->setValue(const_cast(instance), value); + } + + inline bool setStringValue(const std::string& text) + { + return descriptor->setStringValue(const_cast(instance), text); + } + + inline void read(const XmlElement* element, RBX::IReferenceBinder& binder) + { + descriptor->read(const_cast(instance), element, binder); + } + + }; + + std::size_t hash_value(const ConstProperty& prop); + + // Interface + // maps enums to an index (Used by UIs like a property grid) + class RBXInterface EnumPropertyDescriptor : public PropertyDescriptor { + public: + const EnumDescriptor& enumDescriptor; + virtual size_t getIndexValue(const DescribedBase* instance) const = 0; + virtual bool setIndexValue(DescribedBase* instance, size_t value) const = 0; // throws an exception if value is illegal + virtual int getEnumValue(const DescribedBase* instance) const = 0; + virtual bool setEnumValue(DescribedBase* instance, int index) const = 0; + virtual const EnumDescriptor::Item* getEnumItem(const DescribedBase* instance) const = 0; + bool setEnumItem(DescribedBase* instance, const EnumDescriptor::Item& item) const { + if (item.owner!=enumDescriptor) + return false; + return setEnumValue(instance, item.value); + } + virtual int getDataSize(const DescribedBase* instance) const + { return sizeof(int); } + protected: + EnumPropertyDescriptor(ClassDescriptor& classDescriptor, const EnumDescriptor& enumDescriptor, const char* name, const char* category, Attributes flags = STANDARD, Security::Permissions security=Security::None) + :PropertyDescriptor(classDescriptor, enumDescriptor, name, category, flags, security, true) + ,enumDescriptor(enumDescriptor) + {} + }; + + class RBXBaseClass RefPropertyDescriptor : public PropertyDescriptor { + + private: + typedef PropertyDescriptor Super; + + public: + virtual DescribedBase* getRefValue(const DescribedBase* instance) const = 0; + virtual void setRefValue(DescribedBase* instance, DescribedBase* value) const = 0; + virtual void setRefValueUnsafe(DescribedBase* instance, DescribedBase* value) const = 0; + + RefPropertyDescriptor(ClassDescriptor& classDescriptor, const Type& type, const char* name, const char* category, Attributes flags = STANDARD, Security::Permissions security=Security::None) + :PropertyDescriptor(classDescriptor, type, name, category, flags, security) + {} + + virtual int getDataSize(const DescribedBase* instance) const + { return 0; } + + bool hasStringValue() const { + return false; + } + std::string getStringValue(const DescribedBase* instance) const{ + return Super::getStringValue(instance); + } + bool setStringValue(DescribedBase* instance, const std::string& text) const { + return Super::setStringValue(instance, text); + } + + + static bool isRefPropertyDescriptor(const Reflection::Type& type) + { + static const RBX::Name& name = RBX::Name::lookup("Object"); + return (type.name == name); + } + + static bool isRefPropertyDescriptor(const PropertyDescriptor& descriptor) + { + // See RefType in reflection.h + bool result = isRefPropertyDescriptor(descriptor.type); + RBXASSERT(result == (0 != dynamic_cast(&descriptor))); + return result; + } + }; + + // A very useful class for binding Instance members to PropertyDescriptors + template + class BoundProp : public Reflection::TypedPropertyDescriptor + { + template + class BoundPropGetSet : public TypedPropertyDescriptor::GetSet + { + BoundProp& desc; + V Class::*member; + typedef void (Class::*ChangedMember)(const Reflection::PropertyDescriptor&); + ChangedMember changed; + public: + BoundPropGetSet(BoundProp& desc, V Class::*member, ChangedMember changed):desc(desc),member(member),changed(changed) {} + virtual bool isReadOnly() const { + return mutability == READONLY; + } + virtual bool isWriteOnly() const { + return false; + } + virtual V getValue(const Reflection::DescribedBase* object) const { + const Class* c = static_cast(object); + return c->*member; + } + virtual void setValue(Reflection::DescribedBase* object, const V& value) const { + if (mutability == READONLY) + throw std::runtime_error("can't set value"); + + Class* c = static_cast(object); + if (c->*member != value) + { + c->*member = value; + if (changed) + (c->*changed)(desc); + c->raisePropertyChanged(desc); + } + } + }; + public: + template + BoundProp(const char* name, const char* category, V Class::*member, void (Class::*changed)(const Reflection::PropertyDescriptor&), typename PropertyDescriptor::Attributes flags = PropertyDescriptor::STANDARD, Security::Permissions security = Security::None) + :Reflection::TypedPropertyDescriptor(Class::classDescriptor(), name, category, std::auto_ptr::GetSet>(), flags, security) + { + this->getset.reset(new BoundPropGetSet(*this, member, changed)); + this->checkFlags(); + } + template + BoundProp(const char* name, const char* category, V Class::*member, typename PropertyDescriptor::Attributes flags = PropertyDescriptor::STANDARD, Security::Permissions security = Security::None) + :Reflection::TypedPropertyDescriptor(Class::classDescriptor(), name, category, std::auto_ptr::GetSet>(), flags, security) + { + this->getset.reset(new BoundPropGetSet(*this, member, NULL)); + this->checkFlags(); + } + }; + } +} diff --git a/App/reflection/Type.h b/App/reflection/Type.h new file mode 100644 index 0000000..0fede78 --- /dev/null +++ b/App/reflection/Type.h @@ -0,0 +1,323 @@ + +#pragma once + +#include "reflection/Descriptor.h" +#include +#include +#include + +#include +#include + + +namespace RBX +{ + namespace Reflection + { + template class TypeRegistrar; + + // Types supported by the Reflection framework + class Type : public Descriptor + { + template + friend class TypeRegistrar; + + template + static const Type& getSingleton(); // Must be implemented for each type used + void addToAllTypes(); + + public: + const Name& tag; + const bool isFloat; + const bool isNumber; + const bool isEnum; + + static const std::vector& getAllTypes(); + + template + static inline const Type& singleton() + { + return getSingleton(); + } + + bool operator==(const Type& right) const { + return this==&right; + } + bool operator!=(const Type& right) const { + return this!=&right; + } + + template + bool isType() const { + return this == &getSingleton(); + } + + protected: + template + Type(const char* name, T* dummy) + :Descriptor(name, Descriptor::Attributes()) + ,tag(Name::lookup(name)) + ,isNumber(boost::is_arithmetic::value) + ,isFloat(boost::is_float::value) + ,isEnum(false) + { + *isOutdated = false; + *isReplicable = true; + RBXASSERT(!this->tag.empty()); + addToAllTypes(); + } + template + Type(const char* name, const char* tag, T* dummy) + :Descriptor(name, Descriptor::Attributes()) + ,tag(Name::declare(tag)) + ,isNumber(boost::is_arithmetic::value) + ,isFloat(boost::is_float::value) + ,isEnum(false) + { + RBXASSERT(!this->tag.empty()); + addToAllTypes(); + } + + Type(const char* name, const char* tag, bool isNumber, bool isFloat, bool isEnum) + :Descriptor(name, Descriptor::Attributes()) + ,tag(Name::declare(tag)) + ,isNumber(isNumber) + ,isFloat(isFloat) + ,isEnum(isEnum) + { + RBXASSERT(!this->tag.empty()); + addToAllTypes(); + } + }; + + std::ostream& operator<<(std::ostream& os, const RBX::Reflection::Type& type); + + // Handy macro for registering a type +#define RBX_REGISTER_TYPE(mType) template<> RBX::Reflection::TypeRegistrar RBX::Reflection::TypeRegistrar::registrar(0) + + // This class is designed to prevent clients of the library + // from forgetting to initialize their class descriptors + template + class TypeRegistrar : boost::noncopyable + { + int x; + + //// GCC does not generate the registrar variable defination & fails at Link Time. Force Construct by passing in an dummy arg to ctor. That works. WEIRD huh? + TypeRegistrar(int i):x(i) + { + // This assertion is added to catch a nasty implicit use of boost::any with Variant objects. + // If you get a tricky link error, add your own assertion here + BOOST_STATIC_ASSERT((!boost::is_same::value)); + // This call registers the Type descriptor + // in the reflection database + Type::getSingleton(); + } + + public: + // The instantiation of this static member must be in a unit + // that is initialized in the main thread before any objects + // are created. Otherwise the reflection database + // can change at runtime, which would be a disaster + static TypeRegistrar registrar; + }; + + // Helper class + template + class TType : public Type + { + friend class Type; + protected: + TType(const char* name) + :Type(name, (T*)NULL) + { + } + TType(const char* name, const char* tag) + :Type(name, tag, (T*)NULL) + { + } + }; + + class Variant + { + struct Storage + { + char data[96]; + }; + + const Type* _type; + rbx::placement_any value; + + public: + inline Variant() + : _type(&Type::singleton()) + , value() + {} + + inline Variant(const Variant& other) + : _type(other._type) + , value(other.value) + {} + + inline Variant& operator=(const Variant& rhs) + { + _type = rhs._type; + value = rhs.value; + return *this; + } + + template + inline Variant(const ValueType& value) + : _type(&Type::singleton()) + , value(value) + { + } + + template + inline Variant& operator=(const ValueType& rhs) + { + _type = &Type::singleton(); + value = rhs; + return *this; + } + + inline const Type& type() const { + return *_type; + } + + inline bool isVoid() const + { + return *_type==Type::singleton(); + } + inline bool isFloat() const { return type().isFloat; } + inline bool isNumber() const { return type().isNumber; } + inline bool isString() const { return isType();} + + template + inline bool isType() const { + return _type->isType(); + } + + // throws an exception if unable to convert + template + ValueType& convert(); + + // throws an exception if unable to convert + template + inline ValueType get() const + { + if (isType()) + return cast(); + else + { + // Create a non-const copy to extract the value from + Variant v(*this); + return v.convert(); + } + } + + template + inline const T& cast() const { + if (!isType()) + throw std::runtime_error("Variant cast failed"); + return *reinterpret_cast(value.getData()); + } + + template + inline T& cast() { + if (!isType()) + throw std::runtime_error("Variant cast failed"); + return *reinterpret_cast(value.getData()); + } + + template + inline const T* tryCast() const { + if (!isType()) + return NULL; + return reinterpret_cast(value.getData()); + } + + template + inline T* tryCast() { + if (!isType()) + return NULL; + return reinterpret_cast(value.getData()); + } + + private: + template + ValueType& genericConvert(); + + }; + + // Equivalent to an array in Lua + typedef std::vector ValueArray; + + // A limited table in Lua (keys must be strings for now) + typedef boost::unordered_map ValueTable; + + struct Tuple + { + ValueArray values; + Tuple() {} + Tuple(size_t count):values(count) {} + Tuple(const Tuple& other):values(other.values) {} + //Tuple(const ValueArray& values):values(values) {} + Variant& at(size_t i) { return values[i]; } + const Variant& at(size_t i) const { return values[i]; } + }; + + // The same as a ValueTable for now, but will always have a string key. + // TODO: Use boost::unordered_map<> or vector<> instead? + typedef std::map ValueMap; + + // Describes a function's signature + class SignatureDescriptor + { + public: + struct Item { + friend class SignatureDescriptor; + public: + Item(const RBX::Name* name, const Type* type, const Variant& defaultValue); + Item(const RBX::Name* name, const Type* type); + const RBX::Name* name; + const Type* type; + const Variant defaultValue; + bool hasDefaultValue() const + { + return defaultValue.type() == *type; + } + }; + // TODO: Would vector be more efficient? + typedef std::list Arguments; + + const Type* resultType; + Arguments arguments; + + void addArgument(const RBX::Name& name, const Type& type); + void addArgument(const RBX::Name& name, const Type& type, const Variant& defaultValue); + + SignatureDescriptor(); + }; + + template + ValueType& RBX::Reflection::Variant::genericConvert() + { + ValueType* id = tryCast(); + if (id!=NULL) + return *id; + + if (_type->isType()) + { + ValueType v; + if (StringConverter::convertToValue(cast(), v)) + { + value = v; + _type = &Type::singleton(); + return cast(); + } + } + + throw RBX::runtime_error("Unable to cast %s to %s", _type->tag.c_str(), Type::singleton().tag.c_str() ); + } + } +} diff --git a/App/reflection/YieldFunction.h b/App/reflection/YieldFunction.h new file mode 100644 index 0000000..54a3ac1 --- /dev/null +++ b/App/reflection/YieldFunction.h @@ -0,0 +1,65 @@ +#pragma once + +#include "Reflection/Function.h" +#include + +namespace RBX +{ + namespace Reflection + { + class YieldFunction; + + // Base that describes a YieldFunction + class RBXBaseClass YieldFunctionDescriptor : public MemberDescriptor + { + public: + + typedef YieldFunction ConstMember; + typedef YieldFunction Member; + + protected: + SignatureDescriptor signature; + YieldFunctionDescriptor(ClassDescriptor& classDescriptor, const char* name, Security::Permissions security, Attributes attributes); + + public: + const SignatureDescriptor& getSignature() const { return signature; } + virtual void execute(DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const = 0; + }; + + + // A light-weight convenience class that associates a FunctionDescriptor + // with a described object to create a "Function" + class YieldFunction + { + protected: + const YieldFunctionDescriptor* descriptor; + DescribedBase* instance; + public: + inline YieldFunction(const YieldFunctionDescriptor& descriptor, DescribedBase* instance) + :descriptor(&descriptor),instance(instance) + {} + + inline YieldFunction(const YieldFunction& other) + :descriptor(other.descriptor),instance(other.instance) + {} + inline YieldFunction& operator =(const YieldFunction& other) + { + this->descriptor = other.descriptor; + this->instance = other.instance; + return *this; + } + + inline const RBX::Name& getName() const { + return descriptor->name; + } + + inline const YieldFunctionDescriptor* getDescriptor() const { + return descriptor; + } + + void execute(FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const { + return descriptor->execute(const_cast(instance), arguments, resumeFunction, errorFunction); + } + }; + } +} \ No newline at end of file diff --git a/App/reflection/member.h b/App/reflection/member.h new file mode 100644 index 0000000..87eadbb --- /dev/null +++ b/App/reflection/member.h @@ -0,0 +1,353 @@ +#pragma once + +#include "reflection/Descriptor.h" +#include "util/Exception.h" +#include +#include "security/SecurityContext.h" + +#include "rbx/DenseHash.h" + +namespace RBX +{ + namespace Reflection + { + class ClassDescriptor; + class DescribedBase; + + struct StringHashPredicate + { + size_t operator()(const char* s) const; + }; + + struct StringEqualPredicate + { + bool operator()(const char* lhs, const char* rhs) const + { + return strcmp(lhs, rhs) == 0; + } + }; + + // Base class of describing a described object's member: (Member, Event, etc.) + class RBXBaseClass MemberDescriptor : public Descriptor + { + public: + static void (*memberHidingHook)(MemberDescriptor*, MemberDescriptor*); + + // Category is a name used to group properties in the UI + const RBX::Name& category; + + const ClassDescriptor& owner; + const Security::Permissions security; + + protected: + MemberDescriptor(const ClassDescriptor& owner, const char* name, const char* category, Attributes attributes, Security::Permissions security) + :Descriptor(name, attributes) + ,owner(owner) + ,category(RBX::Name::declare(category)) + ,security(security) + { + } + virtual ~MemberDescriptor() {} + public: + bool isMemberOf(const ClassDescriptor& classDescriptor) const; + bool isMemberOf(const DescribedBase* instance) const; + }; + + + class MemberException : public std::runtime_error + { + public: + const MemberDescriptor& desc; + MemberException(const MemberDescriptor& desc, const std::string& _Message) + :std::runtime_error(_Message) + ,desc(desc) + { + } + }; + + template + class MemberDescriptorContainer + { + // Used for sorting + static bool compare(const MemberDescriptorType* a, const MemberDescriptorType* b) + { + return a->name < b->name; + } + public: + class Collection : public std::vector + { + }; + + typedef DenseHashMap DescriptorLookup; + + typedef typename MemberDescriptorType::ConstMember ConstMemberType; + typedef typename MemberDescriptorType::Member MemberType; + class ConstIterator : public std::iterator + { + friend class ClassDescriptor; + const DescribedBase* instance; + typename Collection::const_iterator iter; + public: + ConstIterator(const typename Collection::const_iterator& iter, const DescribedBase* instance) + :iter(iter),instance(instance) + {} + ConstMemberType operator*() const + { // return designated object + return ConstMemberType(**iter, instance); + } + + bool operator==(const ConstIterator& other) const { return iter==other.iter; } + bool operator!=(const ConstIterator& other) const { return iter!=other.iter; } + ConstIterator& operator++() + { // preincrement + ++iter; + return (*this); + } + + ConstIterator operator++(int) + { // postincrement + ConstIterator _Tmp = *this; + ++*this; + return (_Tmp); + } + + const MemberDescriptorType& getDescriptor() const { return **iter; } + }; + + class Iterator : public std::iterator + { + friend class ClassDescriptor; + DescribedBase* instance; + // iter is a const iterator, since we never modifiy the collection of descriptors + typename Collection::const_iterator iter; + public: + Iterator(const typename Collection::const_iterator& iter, DescribedBase* instance) + :iter(iter),instance(instance) + {} + MemberType operator*() const + { // return designated object + return MemberType(**iter, instance); + } + + bool operator==(const Iterator& other) const { return iter==other.iter; } + bool operator!=(const Iterator& other) const { return iter!=other.iter; } + Iterator& operator++() + { // preincrement + ++iter; + return (*this); + } + + Iterator operator++(int) + { // postincrement + Iterator _Tmp = *this; + ++*this; + return (_Tmp); + } + }; + + protected: + Collection descriptors; + DescriptorLookup descriptorLookup; + private: + static Collection& staticData() + { + static Collection result; + return result; + } + static void initStaticData() + { + staticData(); + } + static Collection& allDescriptors() + { + static boost::once_flag flag = BOOST_ONCE_INIT; + boost::call_once(&initStaticData, flag); + return staticData(); + } + + protected: + // This is a list of "subclasses" + std::vector derivedContainers; + + MemberDescriptorContainer* const base; + protected: + MemberDescriptorContainer(MemberDescriptorContainer* base) + :base(base), descriptorLookup("") + { + if (base!=NULL) + { + // Grab base members that have already been declared + mergeMembers(base); + + // Subsequent members declared in a base class will be pushed down in the declare() function + base->derivedContainers.push_back(this); + } + } + + void declareSub(MemberDescriptorType* descriptor, MemberDescriptorType* replaceable) + { + RBXASSERT(replaceable != descriptor); + { + typename Collection::iterator iter = std::lower_bound(descriptors.begin(), descriptors.end(), descriptor, compare); + if (iter == descriptors.end()) + { + descriptors.insert(iter, descriptor); + descriptorLookup[descriptor->name.c_str()] = descriptor; + } + else + { + RBXASSERT(*iter != descriptor); + + if (*iter == replaceable) + { + // Replace it + *iter = descriptor; + descriptorLookup[descriptor->name.c_str()] = descriptor; + } + else if ((*iter)->name != descriptor->name) + { + descriptors.insert(iter, descriptor); + descriptorLookup[descriptor->name.c_str()] = descriptor; + } + else + { + // We've hit upon a member that will hide this member + if (MemberDescriptor::memberHidingHook) + (*MemberDescriptor::memberHidingHook)(descriptor, replaceable); + return; // No need to continue + } + } + } + + // recurse: + for (typename std::vector::iterator iter = derivedContainers.begin(); iter != derivedContainers.end(); ++iter) + (*iter)->declareSub(descriptor, replaceable); + } + public: + void declare(MemberDescriptorType* descriptor) + { + MemberDescriptorType* replaceable = NULL; + + { + typename Collection::iterator iter = std::lower_bound(descriptors.begin(), descriptors.end(), descriptor, compare); + if (iter == descriptors.end()) + { + // add a new one + descriptors.insert(iter, descriptor); + descriptorLookup[descriptor->name.c_str()] = descriptor; + } + else if (*iter == descriptor) + { + // drop out if we've been here before + return; + } + else if ((*iter)->name != descriptor->name) + { + // add a new one + descriptors.insert(iter, descriptor); + descriptorLookup[descriptor->name.c_str()] = descriptor; + } + else + { + // hide a member of a base class + // TODO: Eventually we'd like to nuke this feature, but it is + // required for some legacy things, like BoolValue + replaceable = *iter; + *iter = descriptor; + descriptorLookup[descriptor->name.c_str()] = descriptor; + if (MemberDescriptor::memberHidingHook) + (*MemberDescriptor::memberHidingHook)(descriptor, replaceable); + } + } + + // Also declare this member in sub-classes + for (typename std::vector::iterator iter = derivedContainers.begin(); iter != derivedContainers.end(); ++iter) + (*iter)->declareSub(descriptor, replaceable); + + // Add this to allDescriptors (in a determanistic order) + { + typename Collection::iterator iter = allDescriptors().begin(); + while (iter!=allDescriptors().end()) + { + MemberDescriptorType* desc = *iter; + if (desc==descriptor) + goto SKIP; + int compare = RBX::Name::compare(descriptor->name, desc->name); + if (compare<0) + break; + if (compare==0) + { + // This descriptor name already exists in a different class + compare = RBX::Name::compare(descriptor->owner.name, desc->owner.name); + // Enforce order using class name + if (compare<0) + break; + } + ++iter; + } + allDescriptors().insert(iter, descriptor); +SKIP: ; + } + + } + + ///////////////////////////////////////////////////////////////// + // Type info enumeration + typename Collection::const_iterator descriptors_begin() const { + return descriptors.begin(); + } + typename Collection::const_iterator descriptors_end() const { + return descriptors.end(); + } + size_t descriptor_size() const + { + return descriptors.size(); + } + + ///////////////////////////////////////////////////////////////// + // Enumeration of all descriptors + static typename Collection::const_iterator all_begin() { + return allDescriptors().begin(); + } + static typename Collection::const_iterator all_end() { + return allDescriptors().end(); + } + + ///////////////////////////////////////////////////////////////// + // Type info query + MemberDescriptorType* findDescriptor(const char* name) const + { + MemberDescriptorType* const * item = descriptorLookup.find(name); + + return item ? *item : NULL; + } + + ///////////////////////////////////////////////////////////////// + // Member enumeration + ConstIterator members_begin(const DescribedBase* instance) const { + return ConstIterator(descriptors.begin(), instance); + } + ConstIterator members_end(const DescribedBase* instance) const { + return ConstIterator(descriptors.end(), instance); + } + + Iterator members_begin(DescribedBase* instance) const { + return Iterator(descriptors.begin(), instance); + } + Iterator members_end(DescribedBase* instance) const { + return Iterator(descriptors.end(), instance); + } + protected: + void mergeMembers(const MemberDescriptorContainer* source) + { + for (typename Collection::const_iterator iter = source->descriptors.begin(); iter != source->descriptors.end(); ++iter) + declare(*iter); + + // Recursively merge parent members as well + if (source->base!=NULL) + mergeMembers(source->base); + } + }; + + } +} diff --git a/App/reflection/reflection.h b/App/reflection/reflection.h new file mode 100644 index 0000000..4f859da --- /dev/null +++ b/App/reflection/reflection.h @@ -0,0 +1,2601 @@ +#pragma once +#include "reflection/property.h" +#include "reflection/object.h" +#include "reflection/enumconverter.h" +#include "reflection/Type.h" +#include "rbx/make_shared.h" +#include +#include +#include + +#if defined RBX_PLATFORM_IOS +#define NULL_FUNCTION_PTR typeof(NULL) +#else +#define NULL_FUNCTION_PTR int +#endif +#define _PRISM_PYRAMID_ + +#ifdef RBX_RCC_SECURITY +// data_seg = init rw +// const_seg = init ro +// bss_seg = rw +#define REFLECTION_BEGIN() __pragma(data_seg(".lua")) __pragma(const_seg(".lua")) __pragma(bss_seg(".lua")) +#define REFLECTION_END() __pragma(data_seg()) __pragma(const_seg()) __pragma(bss_seg()) +#else +#define REFLECTION_BEGIN() +#define REFLECTION_END() +#endif + +namespace RBX +{ + namespace Reflection + { + // This class is designed to prevent clients of the library + // from forgetting to initialize their class descriptors + template + class ClassRegistrar : boost::noncopyable + { + int x; + ClassRegistrar(int i):x(i) + { + // This call registers the class descriptor + // in the reflection database + Class::classDescriptor(); + } + public: + void dummy() + { + x++; + } + + // The instantiation of this static member must be in a unit + // that is initialized in the main thread before any objects + // are created. Otherwise the reflection database + // can change at runtime, which would be a disaster + static ClassRegistrar registrar; + }; + // A CRTP class for implementing reflection. Must be a descendant of DescribedBase + template< + class Class, + const char* const& sClassName, + class BaseClass = DescribedBase, + ClassDescriptor::Functionality functionality = Reflection::ClassDescriptor::PERSISTENT, + Security::Permissions security = Security::None + > + class RBXBaseClass Described : public BaseClass + { + void forceRegistration() + { + // Force linking of ClassRegistrar, which will force clients + // of this library to define ClassRegistrar::registrar in + // their startup code. + ClassRegistrar::registrar.dummy(); + } + public: + + static ClassDescriptor& classDescriptor() + { + static ClassDescriptor describedClassDescriptor(BaseClass::classDescriptor(), sClassName, functionality, security); + return describedClassDescriptor; + } + inline Described() { + this->descriptor = &classDescriptor(); + forceRegistration(); + } + template + inline Described(Arg0 arg0):BaseClass(arg0) { + this->descriptor = &classDescriptor(); + forceRegistration(); + } + template + inline Described(Arg0 arg0, Arg1 arg1):BaseClass(arg0, arg1) { + this->descriptor = &classDescriptor(); + forceRegistration(); + } + template + inline Described(Arg0 arg0, Arg1 arg1, Arg2 arg2):BaseClass(arg0, arg1, arg2) { + this->descriptor = &classDescriptor(); + forceRegistration(); + } + template + inline Described(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3):BaseClass(arg0, arg1, arg2, arg3) { + this->descriptor = &classDescriptor(); + forceRegistration(); + } + }; + + + + // Handy macro for registering a class +#define RBX_REGISTER_CLASS(Class) template<> RBX::Reflection::ClassRegistrar RBX::Reflection::ClassRegistrar::registrar(0) + + // This CRTP class puts a property declaration all together. + // It binds a PropertyDescriptor with getter/setter functions. + template + class PropDescriptor : public TypedPropertyDescriptor + { + template + class GetSetImpl : public Reflection::TypedPropertyDescriptor::GetSet + { + Get get; + Set set; + public: + GetSetImpl(Get get, Set set):get(get),set(set) + {} + + /*implement*/ bool isReadOnly() const { return false; } + /*implement*/ bool isWriteOnly() const { return false; } + /*implement*/ V getValue(const DescribedBase* object) const + { + const Class* c = boost::polymorphic_downcast(object); + return (c->*get)(); + } + /*implement*/ void setValue(DescribedBase* object, const V& value) const + { + Class* c = boost::polymorphic_downcast(object); + (c->*set)(value); + } + }; + + template + class GetImpl : public Reflection::TypedPropertyDescriptor::GetSet + { + Get get; + public: + GetImpl(Get get):get(get) + {} + + /*implement*/ bool isReadOnly() const { return true; } + /*implement*/ bool isWriteOnly() const { return false; } + /*implement*/ V getValue(const DescribedBase* object) const + { + const Class* c = boost::polymorphic_downcast(object); + return (c->*get)(); + } + /*implement*/ void setValue(DescribedBase* object, const V& value) const + { + throw std::runtime_error("can't set value"); + } + }; + + template + class SetImpl : public Reflection::TypedPropertyDescriptor::GetSet + { + Set set; + public: + SetImpl(Set set):set(set) + {} + + /*implement*/ bool isReadOnly() const { return false; } + /*implement*/ bool isWriteOnly() const { return true; } + /*implement*/ V getValue(const DescribedBase* object) const + { + throw std::runtime_error("can't get value"); + } + /*implement*/ void setValue(DescribedBase* object, const V& value) const + { + Class* c = boost::polymorphic_downcast(object); + (c->*set)(value); + } + }; + + public: + template + PropDescriptor(const char* name, const char* category, Get get, Set set, PropertyDescriptor::Attributes flags = PropertyDescriptor::Attributes(), Security::Permissions security = Security::None) + :TypedPropertyDescriptor(Class::classDescriptor(), name, category, getset(get, set), flags, security) + { + } + + template + static std::auto_ptr< typename Reflection::TypedPropertyDescriptor::GetSet > getset(Get get, Set set) + { + return std::auto_ptr< typename Reflection::TypedPropertyDescriptor::GetSet >(new GetSetImpl(get, set)); + } + + // Partial specialization for read-only case + template + static std::auto_ptr< typename Reflection::TypedPropertyDescriptor::GetSet > getset(Get get, NULL_FUNCTION_PTR set) + { + return std::auto_ptr< typename Reflection::TypedPropertyDescriptor::GetSet >(new GetImpl(get)); + } + // Partial specialization for write-only case + template + static std::auto_ptr< typename Reflection::TypedPropertyDescriptor::GetSet > getset(NULL_FUNCTION_PTR get, Set set) + { + return std::auto_ptr< typename Reflection::TypedPropertyDescriptor::GetSet >(new SetImpl(set)); + } + }; + + // This CRTP class puts an enum property declaration all together. + // It binds a PropertyDescriptor with getter/setter functions. + // TODO: Refactor: This duplicates code it TypedPropertyDescriptor + template + class EnumPropDescriptor : public EnumPropertyDescriptor + { + std::auto_ptr::GetSet> getset; + const EnumDesc& enumDesc; + public: + template + EnumPropDescriptor(const char* name, const char* category, Get get, Set set, PropertyDescriptor::Attributes flags = PropertyDescriptor::Attributes(), Security::Permissions security=Security::None) + :EnumPropertyDescriptor(Class::classDescriptor(), EnumDesc::singleton(), name, category, flags, security) + ,getset(PropDescriptor::template getset(get, set)) + ,enumDesc(EnumDesc::singleton()) + { + this->checkFlags(); + } + + virtual bool isReadOnly() const { + return getset->isReadOnly(); + } + + virtual bool isWriteOnly() const { + return getset->isWriteOnly(); + } + + /*implement*/ void getVariant(const DescribedBase* instance, Variant& value) const + { + value = getEnumValue(instance); + } + /*implement*/ void setVariant(DescribedBase* instance, const Variant& value) const + { + setEnumValue(instance, value.get()); + } + /*implement*/ void copyValue(const DescribedBase* source, DescribedBase* destination) const + { + setValue(destination, getValue(source)); + } + + V getValue(const DescribedBase* object) const { + return getset->getValue(object); + } + + void setValue(DescribedBase* object, V value) const { + getset->setValue(object, value); + } + + /*implement*/ bool equalValues(const DescribedBase* a, const DescribedBase* b) const { + return getValue(a) == getValue(b); + } + + /*implement*/ const EnumDescriptor::Item* getEnumItem(const DescribedBase* instance) const + { + const EnumDescriptor::Item* result = enumDesc.convertToItem(getValue(instance)); + return result; + } + + /*implement*/ int getEnumValue(const DescribedBase* instance) const + { + return (int) getValue(instance); + } + /*implement*/ bool setEnumValue(DescribedBase* instance, int intValue) const + { + if (enumDesc.isValue(intValue)) + { + setValue(instance, (V) intValue); + return true; + } + else + return false; + } + virtual size_t getIndexValue(const DescribedBase* instance) const + { + return enumDesc.convertToIndex(getValue(instance)); + } + virtual bool setIndexValue(DescribedBase* instance, size_t index) const + { + V value; + if (enumDesc.convertToValue(index, value)) + { + setValue(instance, value); + return true; + } + else + return false; + } + + bool setIntValue(DescribedBase* instance, int intValue) const + { + V v; + if (!enumDesc.mapIntValue(intValue, v)) + return false; + setValue(instance, v); + return true; + } + + // PropertyDescriptor implementation: + virtual bool hasStringValue() const { + return true; + } + virtual std::string getStringValue(const DescribedBase* instance) const + { + return enumDesc.convertToString(getValue(instance)); + } + virtual bool setStringValue(DescribedBase* instance, const std::string& text) const + { + V value; + if (enumDesc.convertToValue(text.c_str(), value)) + { + setValue(instance, value); + return true; + } + else + return false; + } + // An alternate, more efficient version of setStringValue + virtual bool setStringValue(DescribedBase* instance, const RBX::Name& name) const + { + V value; + if (enumDesc.convertToValue(name, value)) + { + setValue(instance, value); + return true; + } + else + return false; + } + + virtual void readValue(DescribedBase* instance, const XmlElement* element, RBX::IReferenceBinder& binder) const + { + if (!element->isXsiNil()) { + + int value; + if (element->getValue(value)) + { + if (setIntValue(instance, value)) + return; + } + + if (element->isValueType()) + { + // Legacy code to handle files older than 10/29/05 + // TODO: Opt: Remove this legacy code sometime? It slows text XML down a bit + std::string sValue; + if (element->getValue(sValue)) + { + V e; + if (enumDesc.convertToValue(sValue.c_str(), e)) + { + setValue(instance, e); + return; + } + if (sValue.size()==0) + { + if (setIndexValue(instance, 0)) + return; + } + } + } + + // TODO: throw error? + RBXASSERT(false); + } + } + virtual void writeValue(const DescribedBase* instance, XmlElement* element) const + { + element->setValue(static_cast(getValue(instance))); + } + }; + + // Helper class + template + class RefType : public Type + { + public: + static const Type& singleton() + { + static RefType type("Object"); + return type; + } + + private: + RefType(const char* name) + :Type(name, "Ref", (T*)NULL) + {} + }; + + template + class RefPropDescriptor + : public RefPropertyDescriptor + , public IIDREF + { + std::auto_ptr::GetSet> getset; + public: + template + RefPropDescriptor(const char* name, const char* category, Get get, Set set, PropertyDescriptor::Attributes attributes = PropertyDescriptor::Attributes(), Security::Permissions security=Security::None) + :RefPropertyDescriptor( + Class::classDescriptor(), + RefType::singleton(), + name, category, + attributes, security + ) + ,getset(PropDescriptor::template getset(get, set)) + { + this->checkFlags(); + } + + virtual bool isReadOnly() const { + return getset->isReadOnly(); + } + + virtual bool isWriteOnly() const { + return getset->isWriteOnly(); + } + + /*implement*/ void getVariant(const DescribedBase* instance, Variant& value) const + { + shared_ptr ref = shared_from(getValue(instance)); + value = ref; + } + /*implement*/ void setVariant(DescribedBase* instance, const Variant& value) const + { + shared_ptr ref = value.get >(); + setRefValue(instance, ref.get()); + } + /*implement*/ void copyValue(const DescribedBase* source, DescribedBase* destination) const + { + setValue(destination, getValue(source)); + } + + RefClass* getValue(const DescribedBase* object) const { + return getset->getValue(object); + } + + void setValue(DescribedBase* object, RefClass* value) const { + getset->setValue(object, value); + } + + /*implement*/ bool equalValues(const DescribedBase* a, const DescribedBase* b) const { + return getValue(a) == getValue(b); + } + + /*implement*/ DescribedBase* getRefValue(const DescribedBase* instance) const { + return getValue(instance); + } + /*implement*/ void setRefValue(DescribedBase* instance, DescribedBase* value) const { + // if value!=NULL then ensure it is the proper type. If value==NULL then it is OK + RefClass* v = value!=NULL ? boost::polymorphic_cast(value) : NULL; + setValue(instance, v); + } + /*implement*/ void setRefValueUnsafe(DescribedBase* instance, DescribedBase* value) const { + setValue(instance, boost::polymorphic_downcast(value)); + } + + void readValue(DescribedBase* instance, const XmlElement* element, IReferenceBinder& binder) const + { + binder.announceIDREF(element, instance, this); + } + void writeValue(const DescribedBase* instance, XmlElement* element) const + { + element->setValue(InstanceHandle(getValue(instance))); + } + + /*implement*/ void assignIDREF(DescribedBase* propertyOwner, const InstanceHandle& handle) const + { + // We know (assume) that this is the right type. If not, then the file is corrupt! + shared_ptr t = handle.getTarget(); + RefClass* r = boost::polymorphic_downcast(t.get()); + setValue(propertyOwner, r); + } + }; + + template + class FuncDesc : public FunctionDescriptor + { + protected: + FuncDesc(const char* name, Security::Permissions security, Attributes attributes) + :FunctionDescriptor(Class::classDescriptor(), name, security, attributes) + { + } + }; + + class ArgHelper + { + template + static bool try_integral(FunctionDescriptor::Arguments& arguments, bool& arg) + { + return arguments.getBool(index, arg); + } + + template + static bool try_integral(FunctionDescriptor::Arguments& arguments, int& arg) + { + long a; + if (!arguments.getLong(index, a)) + return false; + arg = (int) a; + return true; + } + + template + static bool try_integral(FunctionDescriptor::Arguments& arguments, long& arg) + { + return arguments.getLong(index, arg); + } + + template + static inline bool try_integral(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::disable_if >::type* dummy = 0) + { + return false; + } + + template + static bool try_floating_point(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::enable_if >::type* dummy = 0) + { + double a; + if (!arguments.getDouble(index, a)) + return false; + arg = a; + return true; + } + + template + static inline bool try_floating_point(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::disable_if >::type* dummy = 0) + { + return false; + } + + template + static bool try_string(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::enable_if >::type* dummy = 0) + { + return arguments.getString(index, arg); + } + + template + static inline bool try_string(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::disable_if >::type* dummy = 0) + { + return false; + } + + template + static bool try_Vector3int16(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::enable_if >::type* dummy = 0) + { + return arguments.getVector3int16(index, arg); + } + + template + static inline bool try_Vector3int16(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::disable_if >::type* dummy = 0) + { + return false; + } + + template + static bool try_Region3int16(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::enable_if >::type* dummy = 0) + { + return arguments.getRegion3int16(index, arg); + } + + template + static inline bool try_Region3int16(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::disable_if >::type* dummy = 0) + { + return false; + } + + template + static bool try_Vector3(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::enable_if >::type* dummy = 0) + { + return arguments.getVector3(index, arg); + } + + template + static inline bool try_Vector3(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::disable_if >::type* dummy = 0) + { + return false; + } + + template + static bool try_Region3(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::enable_if >::type* dummy = 0) + { + return arguments.getRegion3(index, arg); + } + + template + static inline bool try_Region3(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::disable_if >::type* dummy = 0) + { + return false; + } + + template + static bool try_Rect(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::enable_if >::type* dummy = 0) + { + return arguments.getRect(index, arg); + } + + template + static inline bool try_Rect(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::disable_if >::type* dummy = 0) + { + return false; + } + + template + static bool try_object(FunctionDescriptor::Arguments& arguments, shared_ptr& arg, typename boost::enable_if< boost::is_base_of >::type* dummy = 0) + { + shared_ptr instance; + if (!arguments.getObject(index, instance)) + return false; + arg = shared_static_cast(instance); + return true; + } + + template + static inline bool try_object(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::disable_if > >::type* dummy = 0) + { + return false; + } + + template + static bool try_enum(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::enable_if >::type* dummy = 0) + { + int a; + if (!arguments.getEnum(index, EnumDesc::singleton(), a)) + return false; + arg = (T) a; + return true; + } + + template + static inline bool try_enum(FunctionDescriptor::Arguments& arguments, T& arg, typename boost::disable_if >::type* dummy = 0) + { + return false; + } + + public: + template + static T getArg(FunctionDescriptor::Arguments& arguments, const scoped_ptr& defaultArg, typename boost::disable_if > >::type* dummy = 0) + { + // Use default if no argument exists + if (arguments.size() >= index) + { + // Try using direct argument access + // Good compilers will nicely no-opt all + // but one of these calls: + T arg; + if (try_integral(arguments, arg)) + return arg; + if (try_floating_point(arguments, arg)) + return arg; + if (try_string(arguments, arg)) + return arg; + if (try_enum(arguments, arg)) + return arg; + if (try_object(arguments, arg)) + return arg; + if (try_Vector3int16(arguments, arg)) + return arg; + if (try_Region3int16(arguments, arg)) + return arg; + if (try_Vector3(arguments, arg)) + return arg; + if (try_Region3(arguments, arg)) + return arg; + if (try_Rect(arguments, arg)) + return arg; + + // Fall back to Variant + Variant v; + // nil args return false, so we use the default + // See http://lua-users.org/wiki/TrailingNilParameters + if (arguments.getVariant(index, v)) + return v.convert(); + } + + if (defaultArg) + return *defaultArg; + else + throw RBX::runtime_error("Argument %d missing or nil", index); + } + + // Specialization for Tuple, which takes all arguments >= index + template + static T getArg(FunctionDescriptor::Arguments& arguments, const scoped_ptr& defaultArg, typename boost::enable_if > >::type* dummy = 0) + { + if (arguments.size() < index) + return shared_ptr(); // no arguments. Return an null Tuple, which is shorthand for empty + + // Create a tuple that is the same size as the number of arguments in the stack + shared_ptr tuple = rbx::make_shared(arguments.size() - index + 1); + for (size_t i = 0; i < tuple->values.size(); ++i) + { + // Ignore the result. It might return false for nil args, but that is OK + arguments.getVariant(index + i, tuple->values[i]); + } + // According to http://lua-users.org/wiki/TrailingNilParameters we might + // want to strip trailing nils. + return tuple; + } + }; + + template ::arity > + class BoundFuncDesc; + + /////////////////// + // 0 argument + template + class Call0Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue) + { + returnValue = (o->*function)(); + } + }; + + // Specialization for void return types: + template + class Call0Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue) + { + (o->*function)(); + } + }; + + + // A simple version of FunctionDescriptor for member functions that take no arguments + template + class BoundFuncDesc : public FuncDesc + { + typedef typename boost::function_traits::result_type result_type; + typedef result_type (Class::*FunctionPtr)(); + FunctionPtr function; + + void declareSignature() + { + this->signature.resultType = &Type::singleton(); + } + + + public: + BoundFuncDesc(FunctionPtr function, const char* name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + { + declareSignature(); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments) const + { + Call0Helper::call(boost::polymorphic_downcast(instance),function, arguments.returnValue); + } + }; + + + /////////////////// + // 1 argument + template + class Call1Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1) + { + returnValue = (o->*function)(arg1); + } + }; + + // Specialization for void return types: + template + class Call1Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1) + { + (o->*function)(arg1); + } + }; + + // A simple version of FunctionDescriptor for member functions that take 1 arguments + template + class BoundFuncDesc : public FuncDesc + { + + typedef typename boost::function_traits::result_type result_type; + typedef typename boost::function_traits::arg1_type Arg1; + typedef result_type (Class::*FunctionPtr)(Arg1); + FunctionPtr function; + + const boost::scoped_ptr default1; + + void declareSignature(const char* arg1Name, Variant arg1Default) + { + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + } + + public: + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default1() + { + declareSignature(arg1Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments) const + { + Call1Helper::call(boost::polymorphic_downcast(instance), function, + arguments.returnValue, + ArgHelper::getArg(arguments, default1) + ); + } + }; + + /////////////////// + // 2 arguments + + template + class Call2Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1, const Arg2& arg2) + { + returnValue = (o->*function)(arg1, arg2 ); + } + }; + + // Specialization for void return types: + template + class Call2Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1, const Arg2& arg2) + { + (o->*function)(arg1, arg2); + } + }; + + + // A simple version of FunctionDescriptor for member functions that take 3 arguments + template + class BoundFuncDesc : public FuncDesc + { + typedef typename boost::function_traits::result_type result_type; + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef result_type (Class::*FunctionPtr)(Arg1, Arg2); + FunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + } + + public: + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments) const + { + Call2Helper::call(boost::polymorphic_downcast(instance), + function, + arguments.returnValue, + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2)); + } + }; + + + /////////////////// + // 3 arguments + + template + class Call3Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) + { + returnValue = (o->*function)(arg1, arg2, arg3 ); + } + }; + + // Specialization for void return types: + template + class Call3Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) + { + (o->*function)(arg1, arg2, arg3); + } + }; + + + + // A simple version of FunctionDescriptor for member functions that take 3 arguments + template + class BoundFuncDesc : public FuncDesc + { + typedef typename boost::function_traits::result_type result_type; + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef typename boost::function_traits::arg3_type Arg3; + typedef result_type (Class::*FunctionPtr)(Arg1, Arg2, Arg3); + FunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + const boost::scoped_ptr default3; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default, const char* arg3Name, Variant arg3Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + this->signature.addArgument(RBX::Name::declare(arg3Name), Type::singleton(), arg3Default); + } + + public: + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2, arg3Name, default3); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2, arg3Name, default3); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Arg3 default3, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default3(new Arg3(default3)) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, default3); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments) const + { + Call3Helper::call(boost::polymorphic_downcast(instance), + function, + arguments.returnValue, + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + ArgHelper::getArg(arguments, default3)); + } + }; + + /////////////////// + // 4 arguments + + template + class Call4Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) + { + returnValue = (o->*function)(arg1, arg2, arg3, arg4 ); + } + }; + + // Specialization for void return types: + template + class Call4Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) + { + (o->*function)(arg1, arg2, arg3, arg4); + } + }; + + // A simple version of FunctionDescriptor for member functions that take 4 arguments + template + class BoundFuncDesc : public FuncDesc + { + typedef typename boost::function_traits::result_type result_type; + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef typename boost::function_traits::arg3_type Arg3; + typedef typename boost::function_traits::arg4_type Arg4; + typedef result_type (Class::*FunctionPtr)(Arg1, Arg2, Arg3, Arg4); + FunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + const boost::scoped_ptr default3; + const boost::scoped_ptr default4; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default, const char* arg3Name, Variant arg3Default, const char* arg4Name, Variant arg4Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + this->signature.addArgument(RBX::Name::declare(arg3Name), Type::singleton(), arg3Default); + this->signature.addArgument(RBX::Name::declare(arg4Name), Type::singleton(), arg4Default); + } + + public: + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2, arg3Name, default3, arg4Name, default4); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2, arg3Name, default3, arg4Name, default4); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, default3, arg4Name, default4); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, Arg4 default4, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default4(new Arg4(default4)) + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, default4); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments) const + { + Call4Helper::call(boost::polymorphic_downcast(instance), + function, + arguments.returnValue, + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + ArgHelper::getArg(arguments, default3), + ArgHelper::getArg(arguments, default4)); + + } + }; + + /////////////////// + // 5 arguments + + template + class Call5Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) + { + returnValue = (o->*function)(arg1, arg2, arg3, arg4, arg5 ); + } + }; + + // Specialization for void return types: + template + class Call5Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) + { + (o->*function)(arg1, arg2, arg3, arg4, arg5); + } + }; + + // A simple version of FunctionDescriptor for member functions that take 5 arguments + template + class BoundFuncDesc : public FuncDesc + { + typedef typename boost::function_traits::result_type result_type; + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef typename boost::function_traits::arg3_type Arg3; + typedef typename boost::function_traits::arg4_type Arg4; + typedef typename boost::function_traits::arg5_type Arg5; + typedef result_type (Class::*FunctionPtr)(Arg1, Arg2, Arg3, Arg4, Arg5); + FunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + const boost::scoped_ptr default3; + const boost::scoped_ptr default4; + const boost::scoped_ptr default5; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default, const char* arg3Name, Variant arg3Default, const char* arg4Name, Variant arg4Default, const char* arg5Name, Variant arg5Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + this->signature.addArgument(RBX::Name::declare(arg3Name), Type::singleton(), arg3Default); + this->signature.addArgument(RBX::Name::declare(arg4Name), Type::singleton(), arg4Default); + this->signature.addArgument(RBX::Name::declare(arg5Name), Type::singleton(), arg5Default); + } + + public: + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2, arg3Name, default3, arg4Name, default4, arg5Name, default5); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2, arg3Name, default3, arg4Name, default4, arg5Name, default5); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, default3, arg4Name, default4, arg5Name, default5); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, default4, arg5Name, default5); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, Arg5 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, default5); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default5() + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments) const + { + Call5Helper::call(boost::polymorphic_downcast(instance), + function, + arguments.returnValue, + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + ArgHelper::getArg(arguments, default3), + ArgHelper::getArg(arguments, default4), + ArgHelper::getArg(arguments, default5)); + + } + }; + + /////////////////// + // 6 arguments + + template + class Call6Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5, const Arg6& arg6) + { + returnValue = (o->*function)(arg1, arg2, arg3, arg4, arg5, arg6); + } + }; + + // Specialization for void return types: + template + class Call6Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5, const Arg6& arg6) + { + (o->*function)(arg1, arg2, arg3, arg4, arg5, arg6); + } + }; + + // A simple version of FunctionDescriptor for member functions that take 5 arguments + template + class BoundFuncDesc : public FuncDesc + { + typedef typename boost::function_traits::result_type result_type; + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef typename boost::function_traits::arg3_type Arg3; + typedef typename boost::function_traits::arg4_type Arg4; + typedef typename boost::function_traits::arg5_type Arg5; + typedef typename boost::function_traits::arg6_type Arg6; + typedef result_type (Class::*FunctionPtr)(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6); + FunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + const boost::scoped_ptr default3; + const boost::scoped_ptr default4; + const boost::scoped_ptr default5; + const boost::scoped_ptr default6; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default, const char* arg3Name, Variant arg3Default, const char* arg4Name, Variant arg4Default, const char* arg5Name, Variant arg5Default, const char* arg6Name, Variant arg6Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + this->signature.addArgument(RBX::Name::declare(arg3Name), Type::singleton(), arg3Default); + this->signature.addArgument(RBX::Name::declare(arg4Name), Type::singleton(), arg4Default); + this->signature.addArgument(RBX::Name::declare(arg5Name), Type::singleton(), arg5Default); + this->signature.addArgument(RBX::Name::declare(arg6Name), Type::singleton(), arg6Default); + } + + public: + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, const char* arg6Name, Arg6 default6, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default6(new Arg6(default6)) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2, arg3Name, default3, arg4Name, default4, arg5Name, default5, arg6Name, default6); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, const char* arg6Name, Arg6 default6, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default6(new Arg6(default6)) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2, arg3Name, default3, arg4Name, default4, arg5Name, default5, arg6Name, default6); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, const char* arg6Name, Arg6 default6, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default6(new Arg6(default6)) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, default3, arg4Name, default4, arg5Name, default5, arg6Name, default6); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, const char* arg6Name, Arg6 default6, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default6(new Arg6(default6)) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, default4, arg5Name, default5, arg6Name, default6); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, Arg5 default5, const char* arg6Name, Arg6 default6, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default6(new Arg6(default6)) + ,default5(new Arg5(default5)) + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, default5, arg6Name, default6); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, const char* arg6Name, Arg6 default6, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default6(new Arg6(default6)) + ,default5() + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, Variant(), arg6Name, default6); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, const char* arg6Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default6() + ,default5() + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, Variant(), arg6Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments) const + { + Call6Helper::call(boost::polymorphic_downcast(instance), + function, + arguments.returnValue, + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + ArgHelper::getArg(arguments, default3), + ArgHelper::getArg(arguments, default4), + ArgHelper::getArg(arguments, default5), + ArgHelper::getArg(arguments, default6)); + + } + }; + + /////////////////// + // 7 arguments + + template + class Call7Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5, const Arg6& arg6, const Arg7& arg7) + { + returnValue = (o->*function)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); + } + }; + + // Specialization for void return types: + template + class Call7Helper + { + public: + static void call(Class1* o, FunctionPtr1 function, Variant& returnValue, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5, const Arg6& arg6, const Arg7& arg7) + { + (o->*function)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); + } + }; + + // A simple version of FunctionDescriptor for member functions that take 5 arguments + template + class BoundFuncDesc : public FuncDesc + { + typedef typename boost::function_traits::result_type result_type; + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef typename boost::function_traits::arg3_type Arg3; + typedef typename boost::function_traits::arg4_type Arg4; + typedef typename boost::function_traits::arg5_type Arg5; + typedef typename boost::function_traits::arg6_type Arg6; + typedef typename boost::function_traits::arg7_type Arg7; + typedef result_type (Class::*FunctionPtr)(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7); + FunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + const boost::scoped_ptr default3; + const boost::scoped_ptr default4; + const boost::scoped_ptr default5; + const boost::scoped_ptr default6; + const boost::scoped_ptr default7; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default, const char* arg3Name, Variant arg3Default, const char* arg4Name, Variant arg4Default, const char* arg5Name, Variant arg5Default, const char* arg6Name, Variant arg6Default, const char* arg7Name, Variant arg7Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + this->signature.addArgument(RBX::Name::declare(arg3Name), Type::singleton(), arg3Default); + this->signature.addArgument(RBX::Name::declare(arg4Name), Type::singleton(), arg4Default); + this->signature.addArgument(RBX::Name::declare(arg5Name), Type::singleton(), arg5Default); + this->signature.addArgument(RBX::Name::declare(arg6Name), Type::singleton(), arg6Default); + this->signature.addArgument(RBX::Name::declare(arg7Name), Type::singleton(), arg7Default); + } + + public: + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, const char* arg6Name, Arg6 default6, const char* arg7Name, Arg7 default7, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default7(new Arg7(default7)) + ,default6(new Arg6(default6)) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2, arg3Name, default3, arg4Name, default4, arg5Name, default5, arg6Name, default6, arg7Name, default7); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, const char* arg6Name, Arg6 default6, const char* arg7Name, Arg7 default7, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default7(new Arg7(default7)) + ,default6(new Arg6(default6)) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2, arg3Name, default3, arg4Name, default4, arg5Name, default5, arg6Name, default6, arg7Name, default7); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, const char* arg6Name, Arg6 default6, const char* arg7Name, Arg7 default7, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default7(new Arg7(default7)) + ,default6(new Arg6(default6)) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, default3, arg4Name, default4, arg5Name, default5, arg6Name, default6, arg7Name, default7); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, const char* arg6Name, Arg6 default6, const char* arg7Name, Arg7 default7, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default7(new Arg7(default7)) + ,default6(new Arg6(default6)) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, default4, arg5Name, default5, arg6Name, default6, arg7Name, default7); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, Arg5 default5, const char* arg6Name, Arg6 default6, const char* arg7Name, Arg7 default7, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default7(new Arg7(default7)) + ,default6(new Arg6(default6)) + ,default5(new Arg5(default5)) + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, default5, arg6Name, default6, arg7Name, default7); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, const char* arg6Name, Arg6 default6, const char* arg7Name, Arg7 default7, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default7(new Arg7(default7)) + ,default6(new Arg6(default6)) + ,default5() + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, Variant(), arg6Name, default6, arg7Name, default7); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, const char* arg6Name, const char* arg7Name, Arg7 default7, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,default7(new Arg7(default7)) + ,default6() + ,default5() + ,default4() + ,default3() + ,default2() + ,default1() + ,function(function) + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, Variant(), arg6Name, Variant(), arg7Name, default7); + } + + BoundFuncDesc(FunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, const char* arg6Name, const char* arg7Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :FuncDesc(name, security, attributes) + ,function(function) + ,default7() + ,default6() + ,default5() + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, Variant(), arg6Name, Variant(), arg7Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments) const + { + Call7Helper::call(boost::polymorphic_downcast(instance), + function, + arguments.returnValue, + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + ArgHelper::getArg(arguments, default3), + ArgHelper::getArg(arguments, default4), + ArgHelper::getArg(arguments, default5), + ArgHelper::getArg(arguments, default6), + ArgHelper::getArg(arguments, default7)); + + } + }; + + + template + class YieldFuncDesc : public YieldFunctionDescriptor + { + protected: + YieldFuncDesc(const char* name, Security::Permissions security, Attributes attributes) + :YieldFunctionDescriptor(Class::classDescriptor(), name, security, attributes) + { + } + }; + + template + static void resume_adapter(boost::function resumeFunction, ReturnType returnValue) + { + Variant value = returnValue; + resumeFunction(value); + } + + + template ::result_type, int arity = boost::function_traits::arity > + class BoundYieldFuncDesc; + + // ReturnType() specialization + template + class BoundYieldFuncDesc : public YieldFuncDesc + { + typedef void (Class::*YieldFunctionPtr)(boost::function resumeFunction, boost::function errorFunction); + YieldFunctionPtr function; + + void declareSignature() + { + this->signature.resultType = &Type::singleton(); + } + + public: + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + { + declareSignature(); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const + { + (boost::polymorphic_downcast(instance)->*function)( + boost::bind(&resume_adapter, resumeFunction, _1), errorFunction); + } + }; + + + + // void() specialization + template + class BoundYieldFuncDesc : public YieldFuncDesc + { + typedef void (Class::*YieldFunctionPtr)(boost::function resumeFunction, boost::function errorFunction); + YieldFunctionPtr function; + + void declareSignature() + { + this->signature.resultType = &Type::singleton(); + } + + public: + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + { + declareSignature(); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const + { + (boost::polymorphic_downcast(instance)->*function)( + boost::bind(resumeFunction, Variant()), errorFunction); + } + }; + + + + // A simple version of FunctionDescriptor for member functions that take 1 arguments + template + class BoundYieldFuncDesc : public YieldFuncDesc + { + typedef typename boost::function_traits::arg1_type Arg1; + typedef void (Class::*YieldFunctionPtr)(Arg1, boost::function resumeFunction, boost::function errorFunction); + YieldFunctionPtr function; + + const boost::scoped_ptr default1; + + void declareSignature(const char* arg1Name, Variant arg1Default) + { + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + } + + public: + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default1() + { + declareSignature(arg1Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const + { + (boost::polymorphic_downcast(instance)->*function)( + ArgHelper::getArg(arguments, default1), + boost::bind(&resume_adapter, resumeFunction, _1), errorFunction); + } + }; + + + // void(Arg1) specialization + template + class BoundYieldFuncDesc : public YieldFuncDesc + { + typedef typename boost::function_traits::arg1_type Arg1; + typedef void (Class::*YieldFunctionPtr)(Arg1, boost::function resumeFunction, boost::function errorFunction); + YieldFunctionPtr function; + + const boost::scoped_ptr default1; + + void declareSignature(const char* arg1Name, Variant arg1Default) + { + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + } + + public: + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default1() + { + declareSignature(arg1Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const + { + (boost::polymorphic_downcast(instance)->*function)( + ArgHelper::getArg(arguments, default1), + boost::bind(resumeFunction, Variant()), errorFunction); + } + }; + + + // A simple version of FunctionDescriptor for member functions that take 2 arguments + template + class BoundYieldFuncDesc : public YieldFuncDesc + { + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef void (Class::*YieldFunctionPtr)(Arg1, Arg2, boost::function resumeFunction, boost::function errorFunction); + YieldFunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + } + + public: + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const + { + (boost::polymorphic_downcast(instance)->*function)( + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + boost::bind(&resume_adapter, resumeFunction, _1), errorFunction); + } + }; + + + // void(Arg1, Arg2) specialization + template + class BoundYieldFuncDesc : public YieldFuncDesc + { + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef void (Class::*YieldFunctionPtr)(Arg1, Arg2, boost::function resumeFunction, boost::function errorFunction); + YieldFunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + } + + public: + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const + { + (boost::polymorphic_downcast(instance)->*function)( + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + boost::bind(resumeFunction, Variant()), errorFunction); + } + }; + + + // A simple version of FunctionDescriptor for member functions that take 3 arguments + template + class BoundYieldFuncDesc : public YieldFuncDesc + { + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef typename boost::function_traits::arg3_type Arg3; + typedef void (Class::*YieldFunctionPtr)(Arg1, Arg2, Arg3, boost::function resumeFunction, boost::function errorFunction); + YieldFunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + const boost::scoped_ptr default3; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default, const char* arg3Name, Variant arg3Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + this->signature.addArgument(RBX::Name::declare(arg3Name), Type::singleton(), arg3Default); + } + + public: + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2, arg3Name, default3); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2, arg3Name, default3); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Arg3 default3, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default3(new Arg3(default3)) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, default3); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const + { + (boost::polymorphic_downcast(instance)->*function)( + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + ArgHelper::getArg(arguments, default3), + boost::bind(&resume_adapter, resumeFunction, _1), errorFunction); + } + }; + + + // void(Arg1, Arg2, Arg3) specialization + template + class BoundYieldFuncDesc : public YieldFuncDesc + { + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef typename boost::function_traits::arg3_type Arg3; + typedef void (Class::*YieldFunctionPtr)(Arg1, Arg2, Arg3, boost::function resumeFunction, boost::function errorFunction); + YieldFunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + const boost::scoped_ptr default3; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default, const char* arg3Name, Variant arg3Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + this->signature.addArgument(RBX::Name::declare(arg3Name), Type::singleton(), arg3Default); + } + + public: + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2, arg3Name, default3); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2, arg3Name, default3); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Arg3 default3, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default3(new Arg3(default3)) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, default3); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const + { + (boost::polymorphic_downcast(instance)->*function)( + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + ArgHelper::getArg(arguments, default3), + boost::bind(resumeFunction, Variant()), errorFunction); + } + }; + + // A simple version of FunctionDescriptor for member functions that take 4 arguments + template + class BoundYieldFuncDesc : public YieldFuncDesc + { + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef typename boost::function_traits::arg3_type Arg3; + typedef typename boost::function_traits::arg4_type Arg4; + typedef void (Class::*YieldFunctionPtr)(Arg1, Arg2, Arg3, Arg4, boost::function resumeFunction, boost::function errorFunction); + YieldFunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + const boost::scoped_ptr default3; + const boost::scoped_ptr default4; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default, const char* arg3Name, Variant arg3Default, const char* arg4Name, Variant arg4Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + this->signature.addArgument(RBX::Name::declare(arg3Name), Type::singleton(), arg3Default); + this->signature.addArgument(RBX::Name::declare(arg4Name), Type::singleton(), arg4Default); + } + + public: + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2, arg3Name, default3, arg4Name, default4); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2, arg3Name, default3, arg4Name, default4); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, default3, arg4Name, default4); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, Arg4 default4, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default4(new Arg4(default4)) + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, default4); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const + { + (boost::polymorphic_downcast(instance)->*function)( + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + ArgHelper::getArg(arguments, default3), + ArgHelper::getArg(arguments, default4), + boost::bind(&resume_adapter, resumeFunction, _1), errorFunction); + } + }; + + + // void(Arg1, Arg2, Arg3, Arg4) specialization + template + class BoundYieldFuncDesc : public YieldFuncDesc + { + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef typename boost::function_traits::arg3_type Arg3; + typedef typename boost::function_traits::arg4_type Arg4; + typedef void (Class::*YieldFunctionPtr)(Arg1, Arg2, Arg3, Arg4, boost::function resumeFunction, boost::function errorFunction); + YieldFunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + const boost::scoped_ptr default3; + const boost::scoped_ptr default4; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default, const char* arg3Name, Variant arg3Default, const char* arg4Name, Variant arg4Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + this->signature.addArgument(RBX::Name::declare(arg3Name), Type::singleton(), arg3Default); + this->signature.addArgument(RBX::Name::declare(arg4Name), Type::singleton(), arg4Default); + } + + public: + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2, arg3Name, default3, arg4Name, default4); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2, arg3Name, default3, arg4Name, default4); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, default3, arg4Name, default4); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, Arg4 default4, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default4(new Arg4(default4)) + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, default4); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant()); + } + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const + { + (boost::polymorphic_downcast(instance)->*function)( + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + ArgHelper::getArg(arguments, default3), + ArgHelper::getArg(arguments, default4), + boost::bind(resumeFunction, Variant()), errorFunction); + } + }; + + // A simple version of FunctionDescriptor for member functions that take 4 arguments + template + class BoundYieldFuncDesc : public YieldFuncDesc + { + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef typename boost::function_traits::arg3_type Arg3; + typedef typename boost::function_traits::arg4_type Arg4; + typedef typename boost::function_traits::arg5_type Arg5; + typedef void (Class::*YieldFunctionPtr)(Arg1, Arg2, Arg3, Arg4, Arg5, boost::function resumeFunction, boost::function errorFunction); + YieldFunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + const boost::scoped_ptr default3; + const boost::scoped_ptr default4; + const boost::scoped_ptr default5; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default, const char* arg3Name, Variant arg3Default, const char* arg4Name, Variant arg4Default, const char* arg5Name, Variant arg5Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + this->signature.addArgument(RBX::Name::declare(arg3Name), Type::singleton(), arg3Default); + this->signature.addArgument(RBX::Name::declare(arg4Name), Type::singleton(), arg4Default); + this->signature.addArgument(RBX::Name::declare(arg5Name), Type::singleton(), arg5Default); + } + + public: + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg4 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2, arg3Name, default3, arg4Name, default4, arg5Name, default5); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg4 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2, arg3Name, default3, arg4Name, default4, arg5Name, default5); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, default3, arg4Name, default4, arg5Name, default5); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg4 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, default4, arg5Name, default5); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, Arg4 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, default5); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default5() + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const + { + (boost::polymorphic_downcast(instance)->*function)( + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + ArgHelper::getArg(arguments, default3), + ArgHelper::getArg(arguments, default4), + ArgHelper::getArg(arguments, default5), + boost::bind(&resume_adapter, resumeFunction, _1), errorFunction); + } + }; + + + // void(Arg1, Arg2, Arg3, Arg4, Arg5) specialization + template + class BoundYieldFuncDesc : public YieldFuncDesc + { + typedef typename boost::function_traits::arg1_type Arg1; + typedef typename boost::function_traits::arg2_type Arg2; + typedef typename boost::function_traits::arg3_type Arg3; + typedef typename boost::function_traits::arg4_type Arg4; + typedef typename boost::function_traits::arg5_type Arg5; + typedef void (Class::*YieldFunctionPtr)(Arg1, Arg2, Arg3, Arg4, Arg5, boost::function resumeFunction, boost::function errorFunction); + YieldFunctionPtr function; + + const boost::scoped_ptr default1; + const boost::scoped_ptr default2; + const boost::scoped_ptr default3; + const boost::scoped_ptr default4; + const boost::scoped_ptr default5; + + void declareSignature(const char* arg1Name, Variant arg1Default, const char* arg2Name, Variant arg2Default, const char* arg3Name, Variant arg3Default, const char* arg4Name, Variant arg4Default, const char* arg5Name, Variant arg5Default) + { + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + BOOST_STATIC_ASSERT((!boost::is_same >::value)); + this->signature.resultType = &Type::singleton(); + this->signature.addArgument(RBX::Name::declare(arg1Name), Type::singleton(), arg1Default); + this->signature.addArgument(RBX::Name::declare(arg2Name), Type::singleton(), arg2Default); + this->signature.addArgument(RBX::Name::declare(arg3Name), Type::singleton(), arg3Default); + this->signature.addArgument(RBX::Name::declare(arg4Name), Type::singleton(), arg4Default); + this->signature.addArgument(RBX::Name::declare(arg5Name), Type::singleton(), arg5Default); + } + + public: + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, Arg1 default1, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg4 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1(new Arg1(default1)) + { + declareSignature(arg1Name, default1, arg2Name, default2, arg3Name, default3, arg4Name, default4, arg5Name, default5); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Arg2 default2, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg4 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2(new Arg2(default2)) + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, default2, arg3Name, default3, arg4Name, default4, arg5Name, default5); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Arg3 default3, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg5 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3(new Arg3(default3)) + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, default3, arg4Name, default4, arg5Name, default5); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, Arg4 default4, const char* arg5Name, Arg4 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4(new Arg4(default4)) + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, default4, arg5Name, default5); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, Arg4 default5, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default5(new Arg5(default5)) + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, default5); + } + + BoundYieldFuncDesc(YieldFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, const char* arg5Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + :YieldFuncDesc(name, security, attributes) + ,function(function) + ,default5() + ,default4() + ,default3() + ,default2() + ,default1() + { + declareSignature(arg1Name, Variant(), arg2Name, Variant(), arg3Name, Variant(), arg4Name, Variant(), arg5Name, Variant()); + } + + /*implement*/ void execute(Reflection::DescribedBase* instance, FunctionDescriptor::Arguments& arguments, boost::function resumeFunction, boost::function errorFunction) const + { + (boost::polymorphic_downcast(instance)->*function)( + ArgHelper::getArg(arguments, default1), + ArgHelper::getArg(arguments, default2), + ArgHelper::getArg(arguments, default3), + ArgHelper::getArg(arguments, default4), + ArgHelper::getArg(arguments, default5), + boost::bind(resumeFunction, Variant()), errorFunction); + } + }; + + template ::arity > + class CustomBoundFuncDesc; + + template + class CustomBoundFuncDesc : public BoundFuncDesc + { + typedef int (Class::*CustomFunctionPtr)(lua_State*); + CustomFunctionPtr customFunction; + + public: + CustomBoundFuncDesc(CustomFunctionPtr function, const char* name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + : BoundFuncDesc(NULL, name, security, attributes) + , customFunction(function) + { + this->kind = FunctionDescriptor::Kind_Custom; + } + + /*implement*/ int executeCustom(DescribedBase* instance, lua_State* state) const + { + return (boost::polymorphic_downcast(instance)->*customFunction)(state); + } + }; + + template + class CustomBoundFuncDesc : public BoundFuncDesc + { + typedef int (Class::*CustomFunctionPtr)(lua_State*); + CustomFunctionPtr customFunction; + + public: + CustomBoundFuncDesc(CustomFunctionPtr function, const char* name, const char* arg1Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + : BoundFuncDesc(NULL, name, arg1Name, security, attributes) + , customFunction(function) + { + this->kind = FunctionDescriptor::Kind_Custom; + } + + /*implement*/ int executeCustom(DescribedBase* instance, lua_State* state) const + { + return (boost::polymorphic_downcast(instance)->*customFunction)(state); + } + }; + + template + class CustomBoundFuncDesc : public BoundFuncDesc + { + typedef int (Class::*CustomFunctionPtr)(lua_State*); + CustomFunctionPtr customFunction; + + public: + CustomBoundFuncDesc(CustomFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + : BoundFuncDesc(NULL, name, arg1Name, arg2Name, security, attributes) + , customFunction(function) + { + this->kind = FunctionDescriptor::Kind_Custom; + } + + /*implement*/ int executeCustom(DescribedBase* instance, lua_State* state) const + { + return (boost::polymorphic_downcast(instance)->*customFunction)(state); + } + }; + + template + class CustomBoundFuncDesc : public BoundFuncDesc + { + typedef int (Class::*CustomFunctionPtr)(lua_State*); + CustomFunctionPtr customFunction; + + public: + CustomBoundFuncDesc(CustomFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + : BoundFuncDesc(NULL, name, arg1Name, arg2Name, arg3Name, security, attributes) + , customFunction(function) + { + this->kind = FunctionDescriptor::Kind_Custom; + } + + /*implement*/ int executeCustom(DescribedBase* instance, lua_State* state) const + { + return (boost::polymorphic_downcast(instance)->*customFunction)(state); + } + }; + + template + class CustomBoundFuncDesc : public BoundFuncDesc + { + typedef int (Class::*CustomFunctionPtr)(lua_State*); + CustomFunctionPtr customFunction; + + public: + CustomBoundFuncDesc(CustomFunctionPtr function, const char* name, const char* arg1Name, const char* arg2Name, const char* arg3Name, const char* arg4Name, Security::Permissions security, Descriptor::Attributes attributes = Descriptor::Attributes()) + : BoundFuncDesc(NULL, name, arg1Name, arg2Name, arg3Name, arg4Name, security, attributes) + , customFunction(function) + { + this->kind = FunctionDescriptor::Kind_Custom; + } + + /*implement*/ int executeCustom(DescribedBase* instance, lua_State* state) const + { + return (boost::polymorphic_downcast(instance)->*customFunction)(state); + } + }; + + } +} + diff --git a/App/script/CoreScript.h b/App/script/CoreScript.h new file mode 100644 index 0000000..81df334 --- /dev/null +++ b/App/script/CoreScript.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Script/Script.h" + +#include + +namespace RBX +{ + extern const char* const sCoreScript; + class CoreScript + : public DescribedNonCreatable + { + private: + typedef DescribedNonCreatable Super; + Code code; + + public: + CoreScript(); + + static boost::optional fetchSource(const std::string& name); + + virtual Code requestCode(ScriptInformationProvider* scriptInfoProvider=NULL); + + virtual void extraErrorReporting(lua_State *thread); + + protected: + // Instance + /*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); + }; +} diff --git a/App/script/DebuggerManager.h b/App/script/DebuggerManager.h new file mode 100644 index 0000000..05d0087 --- /dev/null +++ b/App/script/DebuggerManager.h @@ -0,0 +1,400 @@ +#pragma once + +#include "V8Tree/Instance.h" +#include "V8Tree/Service.h" +#include "script/ThreadRef.h" +#include "script/ScriptContext.h" + +struct lua_State; +struct lua_Debug; +struct Table; + +namespace RBX +{ + class Script; + class ModuleScript; + class DataModel; + + namespace Scripting + { + class ScriptDebugger; + + enum BreakOnErrorMode + { + BreakOnErrorMode_Never = 0, + BreakOnErrorMode_AllExceptions, + BreakOnErrorMode_UnhandledExceptions + }; + + enum ExecutionMode + { + ExecutionMode_Continue = 0, + ExecutionMode_Break + }; + + struct ISpecialBreakpoint + { + virtual ~ISpecialBreakpoint() {} + virtual bool hitTest(lua_State* L, lua_Debug *ar) = 0; + lua_State* baseThread; + }; + + extern const char* const sDebuggerManager; + // Contains all data related to Lua debugging of Scripts + class DebuggerManager + : public DescribedNonCreatable + { + typedef DescribedNonCreatable Super; + public: + typedef boost::unordered_map Debuggers; + private: + bool enabled; + Debuggers debuggers; + + rbx::signals::connection errorSignalConnection; + rbx::signals::connection descendantAddedSignalConnection; + + typedef boost::unordered_map > UnaddedDebuggers; + UnaddedDebuggers unaddedDebuggers; + + typedef boost::unordered_map DebuggersLookup; + DebuggersLookup debuggersLookup; + + RBX::DataModel *dataModel; + BreakOnErrorMode breakOnErrorMode; + + boost::scoped_ptr specialBreakpoint; + ExecutionMode executionMode; + std::list pausedThreads, resumingPausedThreads, errorThreads; + bool resuming; + bool scriptAutoResume; + + public: + DebuggerManager(); + ~DebuggerManager(); + + static DebuggerManager& singleton(); + + void setDataModel(RBX::DataModel *pDataModel); + RBX::DataModel* getDataModel(); + + void enableDebugging(); + bool getEnabled() const { return enabled; } + + BreakOnErrorMode getBreakOnErrorMode() const { return breakOnErrorMode; } + void setBreakOnErrorMode(BreakOnErrorMode mode); + + static Reflection::Variant readWatchValue(std::string expression, int stackFrame, lua_State* L); + + const Debuggers& getDebuggers() + { + return debuggers; + } + shared_ptr getDebuggers_Reflection(); + + ScriptDebugger* findDebugger(lua_State* L); + ScriptDebugger* findDebugger(Instance* script); + + shared_ptr addDebugger(Instance* script); + shared_ptr addDebugger_Reflection(shared_ptr script); + void addDebugger(shared_ptr debugger); + + void populateForLookup(lua_State* L, ScriptDebugger* debugger); + + void pause(); + void resume(); + void stepOver(); + void stepInto(); + void stepOut(); + + void reset(); + void setScriptAutoResume(bool state) { scriptAutoResume = state; } + + static void hook(lua_State* L, lua_Debug *ar); + + rbx::signal)> debuggerAdded; + rbx::signal)> debuggerRemoved; + + protected: + /*override*/ bool askForbidChild(const Instance* instance) const; + /*override*/ void verifyAddChild(const Instance* newChild) const; + /*override*/ void onChildAdded(Instance* child); + /*override*/ void onChildRemoved(Instance* child); + /*override*/ void onChildChanged(Instance* instance, const PropertyChanged& event); + + void addScriptDebugger(Instance* instance); + void onErrorSignal(lua_State* L); + void onHook(lua_State* L, lua_Debug *ar); + + void addUnaddedDebuggerForAddedDescendant(shared_ptr instance); + }; + + class DebuggerBreakpoint; + class DebuggerWatch; + + extern const char* const sScriptDebugger; + // Debugs an RBX::Script + class ScriptDebugger + : public DescribedCreatable + { + public: + typedef boost::unordered_map Breakpoints; + typedef std::vector Watches; + struct PausedThreadData; + typedef boost::unordered_map PausedThreads; + private: + typedef DescribedCreatable Super; + + Breakpoints breakpoints; + Watches watches; + + boost::scoped_ptr specialBreakpoint; + + shared_ptr script; + rbx::signals::scoped_connection scriptStartedConnection; + rbx::signals::scoped_connection scriptStoppedConnection; + rbx::signals::scoped_connection scriptParentChangedConnection; + rbx::signals::scoped_connection scriptClonedConnection; + Lua::WeakThreadRef rootThread; // The root thread of script. Set when the Script starts and reset when it stops + + typedef boost::function HookFunction; + HookFunction hookFunction; // used to overload the hook function + + Lua::WeakThreadRef pausedThread; // the thread that a breakpoint hit + Lua::WeakThreadRef errorThread; // the thread that has error + lua_Debug *breakpointHookData; // set for a short period of time during the hook when we encounter a breakpoint + void* globalRawScriptPtr; + Table* prevFuncTable; + void* prevRawScriptPtr; + int currentLine; // the current line when a breakpoint is hit + bool ignoreDebuggerBreak; // whether to ignore breakpoint at the current line + long currentThreadID; + PausedThreads pausedThreads; + bool rootThreadResumed; + + public: + ScriptDebugger() + :currentLine(0) + ,breakpointHookData(NULL) + ,globalRawScriptPtr(NULL) + ,prevFuncTable(NULL) + ,prevRawScriptPtr(NULL) + ,ignoreDebuggerBreak(false) + ,currentThreadID(0) + ,rootThreadResumed(false) + {} + ScriptDebugger(Instance& script); + ~ScriptDebugger(); + + Instance* getScript() const { return script.get(); } + + void setScript(Script* value); + void setScript(ModuleScript* value); + + std::string getScriptPath() const; + void setScriptPath(std::string scriptPath); + + void setIgnoreDebuggerBreak(bool state) { ignoreDebuggerBreak = state; } + + DebuggerBreakpoint* findBreakpoint(int line); + shared_ptr setBreakpoint(int line); + shared_ptr setBreakpoint_Reflection(int line); + const Breakpoints& getBreakpoints() + { + return breakpoints; + } + shared_ptr getBreakpoints_Reflection(); + + shared_ptr addWatch(std::string expression); + shared_ptr addWatch_Reflection(std::string expression); + const Watches& getWatches() + { + return watches; + } + shared_ptr getWatches_Reflection(); + Reflection::Variant getWatchValue(DebuggerWatch* watch, int stackFrame = 0); + Reflection::Variant getWatchValue_Reflection(shared_ptr watch); + + Reflection::Variant getKeyValue(std::string key, int stackFrame); + + bool isDebugging() const + { + return !rootThread.empty(); + } + + bool isPaused() const; + + bool hasError() const + { + return !errorThread.empty(); + } + + int getCurrentLine() const + { + return currentLine; + } + void pause(); + void resume(); + void resumeTo(int line); + void stepOver(); + void stepInto(); + void stepOut(); + + struct FunctionInfo + { + boost::shared_ptr script; + int frame; + std::string name; + std::string what; + std::string namewhat; + std::string short_src; + int currentline; + int linedefined; + int lastlinedefined; + }; + typedef std::vector Stack; + Stack getStack(); + shared_ptr getStack_Reflection(); + shared_ptr getLocals(int stackIndex); + shared_ptr getUpvalues(int stackIndex); + shared_ptr getGlobals(); + void setLocal(std::string name, Reflection::Variant value, int stackFrame = 0); + void setUpvalue(std::string name, Reflection::Variant value, int stackFrame = 0); + void setGlobal(std::string name, Reflection::Variant value); + + void handleError(lua_State* L); + void updateHook(); + + ScriptContext::Result resumeThread(lua_State* L, bool evalLineHookForCurrentLine = false); + bool handleHook(lua_State* L, lua_Debug *ar); + bool onLineHook(lua_State* L, lua_Debug *ar); + void debuggerBreak(lua_State* L, lua_Debug *ar); + + struct PausedThreadData + { + int pausedLine; + Lua::WeakThreadRef thread; + + bool hasError; + Stack callStack; + std::string errorMessage; + + PausedThreadData() + :hasError(false) + ,pausedLine(0) + { + } + }; + const PausedThreads& getPausedThreads() { return pausedThreads; } + + bool isPausedThread(long threadID); + bool isErrorThread(long threadID); + + bool isRootThread(long threadID); + bool isRootThreadResumed() { return rootThreadResumed; } + + void setCurrentThread(long threadID); + long getCurrentThread() { return currentThreadID; } + + rbx::signal encounteredBreak; + rbx::signal resuming; + + rbx::signal)> breakpointAdded; + rbx::signal)> breakpointRemoved; + rbx::signal)> watchAdded; + rbx::signal)> watchRemoved; + rbx::signal scriptErrorDetected; + + protected: + /*override*/ bool askForbidChild(const Instance* instance) const; + /*override*/ void verifySetParent(const Instance* newParent) const; + /*override*/ void verifyAddChild(const Instance* newChild) const; + /*override*/ void onChildAdded(Instance* child); + /*override*/ void onChildRemoved(Instance* child); + + private: + void onScriptStarting(lua_State* L); + void onScriptStopped(); + void onScriptParentChanged(shared_ptr newParent); + void onScriptCloned(boost::shared_ptr clonedScript); + bool shouldBreak(DebuggerBreakpoint* bp, lua_State* L); + bool hasDifferentScriptInstances(lua_State* L); + void handleError(std::string errorMessage, const Stack& stack = Stack()); + void setScript(Instance* value); + boost::shared_ptr createClone(boost::shared_ptr clonedScript); + + template + void withPausedThreadHook(lua_State* L, lua_Debug *ar, boost::function f, R& r, shared_ptr& error); + + // TODO: template specialization for R=void + template + R withPausedThread(boost::function f); + static shared_ptr readLocals(int stackIndex, lua_State* L); + static shared_ptr readUpvalues(int stackIndex, lua_State* L); + static shared_ptr readGlobals(lua_State* L); + static Stack readStack(lua_State* L); + static RBX::Instance* getScriptForLuaState(lua_State* L); + static void updateRootThread(ScriptDebugger* scriptDebugger, lua_State *L); + static void setLuaHook(ScriptDebugger* scriptDebugger, int hookMask, lua_State *L); + }; + + extern const char* const sDebuggerBreakpoint; + class DebuggerBreakpoint + : public DescribedCreatable + { + bool enabled; + int line; + std::string condition; + public: + DebuggerBreakpoint(); + DebuggerBreakpoint(int line); + ~DebuggerBreakpoint(); + + int getLine() const { return line; } + + bool isEnabled() const { return enabled; } + + const std::string& getCondition() const { return condition; } + + static Reflection::BoundProp prop_Enabled; + static Reflection::BoundProp prop_Condition; + + protected: + /*override*/ void verifySetParent(const Instance* newParent) const; + /*override*/ bool askForbidChild(const Instance* instance) const { return true; } + /*override*/ void verifyAddChild(const Instance* newChild) const + { + throw std::runtime_error("DebuggerBreakpoint can have no children"); + } + private: + void setLine(int line); + static Reflection::BoundProp prop_Line_Data; + + }; + + extern const char* const sDebuggerWatch; + class DebuggerWatch + : public DescribedCreatable + { + std::string expression; + public: + DebuggerWatch() {} + DebuggerWatch(std::string expression); + const std::string& getCondition() const { return expression; } + void checkExpressionSyntax(); + + const std::string& getExpression() const { return expression; } + + static Reflection::BoundProp prop_Expression; + + protected: + /*override*/ void verifySetParent(const Instance* newParent) const; + /*override*/ bool askForbidChild(const Instance* instance) const { return true; } + /*override*/ void verifyAddChild(const Instance* newChild) const + { + throw std::runtime_error("DebuggerWatch can have no children"); + } + }; + } +} + diff --git a/App/script/ExitHandlers.h b/App/script/ExitHandlers.h new file mode 100644 index 0000000..db95cf2 --- /dev/null +++ b/App/script/ExitHandlers.h @@ -0,0 +1,41 @@ +#pragma once + +#include "boost/function.hpp" +#include "reflection/Type.h" + +struct lua_State; + +namespace RBX +{ + class BaseScript; + + namespace Scripts + { + typedef boost::function results)> SuccessHandler; + typedef boost::function source, int line)> ErrorHandler; + struct Continuations + { + SuccessHandler successHandler; + ErrorHandler errorHandler; + bool empty() const + { + return successHandler.empty() && errorHandler.empty(); + } + }; + } + + namespace Lua + { + class Continuations + { + public: + Continuations(const Scripts::Continuations& eh); + Continuations() {} + boost::function success; // called when the thread exits via ScriptContext::resume + boost::function error; // called when the thread errors via ScriptContext::resume + private: + static void onSuccessHandler(lua_State* thread, Scripts::SuccessHandler handler); + static void onErrorHandler(lua_State* thread, Scripts::ErrorHandler handler); + }; + } +} \ No newline at end of file diff --git a/App/script/IScriptFilter.h b/App/script/IScriptFilter.h new file mode 100644 index 0000000..a92ab81 --- /dev/null +++ b/App/script/IScriptFilter.h @@ -0,0 +1,52 @@ +#pragma once + +#include "util/RunStateOwner.h" + +namespace RBX +{ + class BaseScript; + class ModuleScript; + + // Interface for Instances that turn in-game Scripts on and off + // Implementations can add/remove the script from ScriptContext + class RBXInterface IScriptFilter + { + friend class BaseScript; + protected: + // If script should run - pass back the IScriptOwner who should run it, otherwise NULL + virtual bool scriptShouldRun(BaseScript* script) = 0; + }; + + extern const char *const sRuntimeScriptService; + + class RuntimeScriptService + : public DescribedNonCreatable + , public Service + { + private: + typedef DescribedNonCreatable Super; + + public: + RuntimeScriptService():isRunning(false) + { + } + + void runScript(BaseScript* script); + void releaseScript(BaseScript* script); + + protected: + virtual void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); + private: + rbx::signals::scoped_connection runTransitionConnection; + std::set > pendingScripts; // holds Scripts that are waiting for "Run" + std::set > runningScripts; + + bool isRunning; + + void onRunTransition(RunTransition event) + { + onRunState(event.newState); + } + void onRunState(RunState state); + }; +} diff --git a/App/script/LuaArguments.h b/App/script/LuaArguments.h new file mode 100644 index 0000000..0e0ef38 --- /dev/null +++ b/App/script/LuaArguments.h @@ -0,0 +1,249 @@ +#pragma once + +#include "reflection/Function.h" +#include "script/LuaAtomicClasses.h" +#include "script/LuaEnum.h" +#include "script/ThreadRef.h" +#include "script/LuaInstanceBridge.h" +#include "rbx/make_shared.h" +#include "rbx/DenseHash.h" +#include "util/ProtectedString.h" +#include "util/PhysicalProperties.h" + +namespace RBX { + + // Utility function that expands a variant to a strongly-typed value + template + R withVariantValue(const Reflection::Variant& value, F f) + { + if (value.isType()) + return f(); + + if (value.isType()) + return f(value.cast()); + + if (value.isType()) + return f(value.cast()); + + if (value.isType()) + return f(value.cast()); + + if (value.isType()) + return f(value.cast()); + + if (value.isType()) + return f(value.cast()); + + if (value.isType()) + return f(value.cast()); + + if (value.isType()) + return f(value.cast()); + + if (value.isType< shared_ptr >()) + return f(value.cast >()); + + if (const Reflection::EnumDescriptor* desc = Reflection::EnumDescriptor::lookupDescriptor(value.type())) + { + const Reflection::EnumDescriptor::Item* item = desc->lookup(value); + if (item == NULL) + throw RBX::runtime_error("Invalid value for enum %s", desc->name.c_str()); + return f(*item); + } + + if (value.isType()) + return f(value.cast()); + + if (value.isType >()) + return f(value.cast >()); + + if (value.isType >()) + return f(value.cast >()); + + if (value.isType >()) + return f(value.cast >()); + + if (value.isType >()) + return f(value.cast >()); + + if (value.isType >()) + return f(value.cast >()); + + if (value.isType< shared_ptr >()) + return f(value.cast< shared_ptr >()); + + if (value.isType< shared_ptr >()) + return f(value.cast< shared_ptr >()); + + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if( value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + + if (value.isType()) + return f(*value.cast()); + + if (value.isType()) + return f(value.cast()); + + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + if (value.isType()) + return f(value.cast()); + + RBXASSERT(0); + return f(); + } + + + + + + namespace Lua { + + class LuaArguments : public Reflection::FunctionDescriptor::Arguments + { + typedef DenseHashMap TablesCollection; + static bool getRec(lua_State *L, int luaIndex, Reflection::Variant& value, bool treatNilAsMissing, TablesCollection* visitedTables = NULL); + + const int offset; + lua_State * const L; + public: + LuaArguments(lua_State *L, int offset):L(L),offset(offset) {} + + virtual size_t size() const { + return lua_gettop(L) - 1; + } + + // Gets all arguments from the stack and puts them into the ValueArray + static shared_ptr getValues(lua_State* L) + { + int argCount = lua_gettop(L); + + shared_ptr args(rbx::make_shared(argCount)); + + for (int i = 0; ivalues.at(i); + bool success = RBX::Lua::LuaArguments::get(L, i+1, v, false); + RBXASSERT(success); + } + + return args; + } + + static int pushTuple(const Reflection::Tuple& arguments, lua_State* L) + { + return pushValues(arguments.values, L); + } + + // pushes all the values from the ValueArray onto the stack + static int pushValues(const Reflection::ValueArray& arguments, lua_State* L) + { + int argCount = 0; + Reflection::ValueArray::const_iterator end = arguments.end(); + for (Reflection::ValueArray::const_iterator iter = arguments.begin(); iter != end; ++iter) + { + argCount += push(*iter, L); + } + return argCount; + } + + ////////////////////////////////////// + // Implemenent virtual functions + // + // Place the value for the requested parameter in "value". + // + // index: 1-based index into the argument list + // value: the value to set. If index >= size(), then value is unchanged + /*implement*/ bool getVariant(int index, Reflection::Variant& value) const { + const int luaIndex = index + offset; + RBXASSERT(luaIndex>0); + return get(L, luaIndex, value, true); + } + /*implement*/ bool getLong(int index, long& value) const + { + // All numbers in Lua are double, so just call the double version of get + double v; + if (getDouble(index, v)) + { + value = G3D::iRound(v); + return true; + } + return false; + } + /*implement*/ bool getDouble(int index, double& value) const; + /*implement*/ bool getObject(int index, shared_ptr& value) const; + /*implement*/ bool getBool(int index, bool& value) const; + /*implement*/ bool getString(int index, std::string& value) const; + /*implement*/ bool getVector3(int index, Vector3& value) const; + /*implement*/ bool getRegion3(int index, Region3& value) const; + /*implement*/ bool getVector3int16(int index, Vector3int16& value) const; + /*implement*/ bool getRegion3int16(int index, Region3int16& value) const; + /*implement*/ bool getRect(int index, Rect2D& value) const; + /*implement*/ bool getPhysicalProperties(int index, PhysicalProperties& value) const; + /*implement*/ bool getEnum(int index, const Reflection::EnumDescriptor& desc, int& value) const; + // + ////////////////////////////////////// + + // Gets a value from the Lua stack. Returns false if no value is found + static bool get(lua_State *L, int luaIndex, Reflection::Variant& value, bool treatNilAsMissing); + template + static int pushArray(_InIt _First, _InIt _Last, lua_State * const L) { + lua_createtable(L, _Last - _First, 0); + unsigned int i = 0; + while (_First!=_Last) + { + int count = push(*_First, L); + RBXASSERT(count == 1); // If not 1, then what do we do? + lua_rawseti(L, -2, ++i); + ++_First; + } + return 1; + } + + static int push(const Reflection::Variant& value, lua_State * const L); + + static int pushReturnValue(const Reflection::Variant& value, lua_State * const L); + static shared_ptr convertToReturnValues(const Reflection::Variant& value); + }; + +}} diff --git a/App/script/LuaAtomicClasses.h b/App/script/LuaAtomicClasses.h new file mode 100644 index 0000000..e7a763b --- /dev/null +++ b/App/script/LuaAtomicClasses.h @@ -0,0 +1,402 @@ +#pragma once + +#include "Lua/LuaBridge.h" +#include "util/G3DCore.h" +#include "g3d/Color3.h" +#include "g3d/CoordinateFrame.h" +#include "g3d/Vector3.h" +#include "g3d/Vector3int16.h" +#include "RbxG3D/RbxRay.h" +#include "Util/BrickColor.h" +#include "Util/UDim.h" +#include "Util/Region3.h" +#include "Util/Region3int16.h" +#include "Util/Faces.h" +#include "Util/Axes.h" +#include "Util/CellID.h" +#include "util/PhysicalProperties.h" +#include "v8datamodel/NumberSequence.h" +#include "v8datamodel/ColorSequence.h" +#include "v8datamodel/NumberRange.h" + +namespace RBX { namespace Lua { + + class CoordinateFrameBridge : public Bridge + { + friend class Bridge< G3D::CoordinateFrame >; + public: + static void registerClassLibrary (lua_State *L); + + static void pushCoordinateFrame(lua_State *L, const G3D::CoordinateFrame& CF) + { + pushNewObject(L, CF); + } + private: + static int newCoordinateFrame(lua_State *L); + static int fromEulerAnglesXYZ(lua_State *L); + static int fromAxisAngle(lua_State *L); + static int on_add(lua_State *L); + static int on_sub(lua_State *L); + static int on_mul(lua_State *L); + static int on_inverse(lua_State *L); + static int on_lerp(lua_State *L); + + // Implementation of G3D::CoordinateFrame help functions + static int on_toWorldSpace(lua_State *L); + static int on_toObjectSpace(lua_State *L); + static int on_pointToWorldSpace(lua_State *L); + static int on_pointToObjectSpace(lua_State *L); + static int on_vectorToWorldSpace(lua_State *L); + static int on_vectorToObjectSpace(lua_State *L); + static int on_toEulerAnglesXYZ(lua_State *L); + static int on_components(lua_State *L); + + static const luaL_reg classLibrary[]; + }; + + class PhysicalPropertiesBridge : public Bridge + { + friend class Bridge; + public: + static void registerClassLibrary (lua_State *L); + + static void pushPhysicalProperties(lua_State *L, const PhysicalProperties& v) + { + if (v.getCustomEnabled() == true) + { + pushNewObject(L, v); + } + else + { + lua_pushnil(L); + } + } + private: + static int newPhysicalProperties(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + class Rect2DBridge : public Bridge + { + friend class Bridge< G3D::Rect2D >; + public: + static void registerClassLibrary (lua_State *L); + + static void pushRect2D(lua_State *L, const G3D::Rect2D& v) + { + pushNewObject(L, v); + } + private: + static int newRect2D(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + class Region3Bridge : public Bridge + { + friend class Bridge< RBX::Region3 >; + public: + static void registerClassLibrary (lua_State *L) ; + + static void pushRegion3(lua_State *L, const RBX::Region3& v) + { + pushNewObject(L, v); + } + private: + static int newRegion3(lua_State *L); + static int expandToGrid(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + class Region3int16Bridge : public Bridge + { + friend class Bridge< RBX::Region3int16 >; + public: + static void registerClassLibrary (lua_State *L); + + static void pushRegion3int16(lua_State *L, const RBX::Region3int16& v) + { + pushNewObject(L, v); + } + private: + static int newRegion3int16(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + class Vector3Bridge : public Bridge + { + friend class Bridge< G3D::Vector3 >; + public: + static void registerClassLibrary (lua_State *L); + + static void pushVector3(lua_State *L, const G3D::Vector3& v) + { + pushNewObject(L, v); + } + private: + static int newVector3(lua_State *L); + static int newVector3FromNormalId(lua_State *L); + static int newVector3FromAxis(lua_State *L); + static int on_add(lua_State *L); + static int on_sub(lua_State *L); + static int on_mul(lua_State *L); + static int on_div(lua_State *L); + static int on_unm(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + class Vector3int16Bridge : public Bridge + { + friend class Bridge< G3D::Vector3int16 >; + public: + static void registerClassLibrary (lua_State *L); + + static void pushVector3int16(lua_State *L, const G3D::Vector3int16& v) + { + pushNewObject(L, v); + } + private: + static int newVector3int16(lua_State *L); + static int on_add(lua_State *L); + static int on_sub(lua_State *L); + static int on_mul(lua_State *L); + static int on_div(lua_State *L); + static int on_unm(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + class RbxRayBridge : public Bridge + { + friend class Bridge< RBX::RbxRay >; + public: + static void registerClassLibrary (lua_State *L); + + static void pushRay(lua_State *L, const RBX::RbxRay& v) + { + pushNewObject(L, v); + } + private: + static int newRbxRay(lua_State *L); + //static int on_add(lua_State *L); + //static int on_sub(lua_State *L); + //static int on_mul(lua_State *L); + //static int on_div(lua_State *L); + //static int on_unm(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + + class Vector2Bridge : public Bridge + { + friend class Bridge< RBX::Vector2 >; + public: + static void registerClassLibrary (lua_State *L); + + static void pushVector2(lua_State *L, const RBX::Vector2& v) + { + pushNewObject(L, v); + } + private: + static int newVector2(lua_State *L); + static int on_add(lua_State *L); + static int on_sub(lua_State *L); + static int on_mul(lua_State *L); + static int on_div(lua_State *L); + static int on_unm(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + class Vector2int16Bridge : public Bridge + { + friend class Bridge< RBX::Vector2int16 >; + public: + static void registerClassLibrary (lua_State *L); + + static void pushVector2int16(lua_State *L, const RBX::Vector2int16& v) + { + pushNewObject(L, v); + } + private: + static int newVector2int16(lua_State *L); + static int on_add(lua_State *L); + static int on_sub(lua_State *L); + static int on_mul(lua_State *L); + static int on_div(lua_State *L); + static int on_unm(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + class Color3Bridge : public Bridge + { + friend class Bridge< G3D::Color3 >; + public: + static void registerClassLibrary (lua_State *L); + static void pushColor3(lua_State *L, const G3D::Color3& color); + + private: + static int newColor3(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + class UDimBridge : public Bridge + { + friend class Bridge< RBX::UDim>; + public: + static void registerClassLibrary (lua_State *L); + + static void pushUDim(lua_State *L, const RBX::UDim& v) + { + pushNewObject(L, v); + } + private: + static int newUDim(lua_State *L); + static int on_add(lua_State *L); + static int on_sub(lua_State *L); + static int on_unm(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + class UDim2Bridge : public Bridge + { + friend class Bridge< RBX::UDim2>; + public: + static void registerClassLibrary (lua_State *L); + + private: + static int newUDim2 (lua_State *L); + static int on_add(lua_State *L); + static int on_sub(lua_State *L); + static int on_unm(lua_State *L); + + static const luaL_reg classLibrary[]; + }; + + class FacesBridge : public Bridge + { + friend class Bridge< RBX::Faces>; + public: + static void registerClassLibrary (lua_State *L); + + private: + static int newFaces (lua_State *L); + static const luaL_reg classLibrary[]; + }; + + class AxesBridge : public Bridge + { + friend class Bridge< RBX::Axes>; + public: + static void registerClassLibrary (lua_State *L); + + private: + static int newAxes(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + class BrickColorBridge : public Bridge + { + friend class Bridge< RBX::BrickColor >; + public: + static void registerClassLibrary (lua_State *L) ; + + private: + static int newBrickColor(lua_State *L); + static int randomBrickColor(lua_State *L); + static int paletteBrickColor(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + // CellID bridge for cluster access + class CellIDBridge : public Bridge + { + friend class Bridge< CellID >; + public: + static void registerClassLibrary (lua_State *L) ; + + static void pushCellID(lua_State *L, const CellID& v) + { + pushNewObject(L, v); + } + private: + static int newCellID(lua_State *L); + static const luaL_reg classLibrary[]; + }; + + // Number sequence for particle props + class NumberSequenceBridge : public Bridge + { + friend class Bridge< NumberSequence >; + public: + static void registerClassLibrary(lua_State* L); + static void pushNumberSequence(lua_State* L, const NumberSequence& v) { pushNewObject(L, v); } + private: + static int newNumberSequence(lua_State* L); + static const luaL_reg classLibrary[]; + }; + + // Number sequence for particle props + class ColorSequenceBridge : public Bridge + { + friend class Bridge< ColorSequence >; + public: + static void registerClassLibrary(lua_State* L); + static void pushColorSequence(lua_State* L, const ColorSequence& v) { pushNewObject(L, v); } + private: + static int newColorSequence(lua_State* L); + static const luaL_reg classLibrary[]; + }; + + class NumberSequenceKeypointBridge : public Bridge + { + friend class Bridge< NumberSequenceKeypoint >; + public: + static void registerClassLibrary(lua_State* L); + static void pushNumberSequenceKeypoint(lua_State* L, const NumberSequenceKeypoint& v) { pushNewObject(L, v); } + private: + static int newNumberSequenceKeypoint(lua_State* L); + static const luaL_reg classLibrary[]; + }; + + class ColorSequenceKeypointBridge : public Bridge + { + friend class Bridge< ColorSequenceKeypoint >; + public: + static void registerClassLibrary(lua_State* L); + static void pushColorSequenceKeypoint(lua_State* L, const ColorSequenceKeypoint& v) { pushNewObject(L, v); } + private: + static int newColorSequenceKeypoint(lua_State* L); + static const luaL_reg classLibrary[]; + }; + + class NumberRangeBridge : public Bridge + { + friend class Bridge< NumberRange >; + public: + static void registerClassLibrary(lua_State* L); + static void pushNumberRange(lua_State* L, const NumberRange& v) { pushNewObject(L, v); } + private: + static int newNumberRange(lua_State* L); + static const luaL_reg classLibrary[]; + }; + +// Specialization to implement arithmatic operators +template<> +void Bridge::registerClass (lua_State *L); + +template<> +void Bridge::registerClass (lua_State *L); + +template<> +void Bridge::registerClass (lua_State *L); + +template<> +void Bridge::registerClass (lua_State *L); + +// Specialization to implement arithmatic operators +template<> +void Bridge::registerClass (lua_State *L); + +// Specialization to implement arithmatic operators +template<> +void Bridge::registerClass (lua_State *L); + +} } diff --git a/App/script/LuaCoreFunctions.h b/App/script/LuaCoreFunctions.h new file mode 100644 index 0000000..9aa078d --- /dev/null +++ b/App/script/LuaCoreFunctions.h @@ -0,0 +1,19 @@ +#pragma once + +#include "lauxlib.h" + +namespace LuaOsExtension +{ + extern const luaL_Reg registry[]; +} + +namespace LuaMathExtension +{ + int noise(lua_State* L); +} + +namespace LuaDebugExtension +{ + extern const luaL_Reg registry[]; +} + diff --git a/App/script/LuaEnum.h b/App/script/LuaEnum.h new file mode 100644 index 0000000..32bf591 --- /dev/null +++ b/App/script/LuaEnum.h @@ -0,0 +1,56 @@ +#pragma once + +#include "Lua/LuaBridge.h" +#include "reflection/enumConverter.h" +#include "rbxformat.h" + +namespace RBX { namespace Lua { + + class AllEnumDescriptors + { + }; + typedef const AllEnumDescriptors* AllEnumDescriptorsPtr; + + // Represents a Reflection::EnumDescriptor::Item in Lua + class Enums : public SingletonBridge + { + public: + static void declareAllEnums(lua_State *L); + static bool getValue(lua_State *L, unsigned int index, RBX::Reflection::Variant& value); + }; + + typedef const Reflection::EnumDescriptor* EnumDescriptorPtr; + + // Represents a Reflection::EnumDescriptor::Item in Lua + class Enum : public SingletonBridge + { + public: + }; + + typedef const Reflection::EnumDescriptor::Item* EnumDescriptorItemPtr; + + // Represents a Reflection::EnumDescriptor::Item in Lua + class EnumItem : public SingletonBridge + { + public: + static EnumDescriptorItemPtr getItem(lua_State *L, unsigned int index) { + return getObject(L, index); + } + static bool getItem(lua_State *L, unsigned int index, EnumDescriptorItemPtr& value) { + return getValue(L, index, value); + } + }; + + // specialization + template<> + int Bridge< AllEnumDescriptorsPtr, false >::on_tostring(const AllEnumDescriptorsPtr& object, lua_State *L); + + // specialization + template<> + int Bridge< EnumDescriptorPtr, false >::on_tostring(const EnumDescriptorPtr& object, lua_State *L); + + // specialization + template<> + int Bridge< EnumDescriptorItemPtr, false >::on_tostring(const EnumDescriptorItemPtr& object, lua_State *L); + +} } diff --git a/App/script/LuaInstanceBridge.h b/App/script/LuaInstanceBridge.h new file mode 100644 index 0000000..def330e --- /dev/null +++ b/App/script/LuaInstanceBridge.h @@ -0,0 +1,41 @@ + +#pragma once +#include "Lua/LuaBridge.h" +#include "V8Tree/Instance.h" + +namespace RBX { namespace Lua { + + // specialization + template<> + int Bridge< shared_ptr, false >::on_tostring(const shared_ptr& object, lua_State *L); + + class ObjectBridge : public SharedPtrBridge + { + friend class SharedPtrBridge; + static const luaL_reg classLibrary[]; + public: + static int callMemberFunction(lua_State *L); + static int callMemberYieldFunction(lua_State *L); + + static void registerInstanceClassLibrary (lua_State *L) { + + // Register the "new" function for Instances + luaL_register(L, "Instance", classLibrary); + lua_setreadonly(L, -1, true); + lua_pop(L,1); // Pop table from stack. http://lua-users.org/lists/lua-l/2003-12/msg00139.html + } + + static int newInstance(lua_State *L); + static int lockInstance(lua_State *L); + static int unlockInstance(lua_State *L); + + static boost::shared_ptr getInstance(lua_State *L, unsigned int index) + { + return getPtr(L, index); + } + }; + + template<> + void Bridge< shared_ptr, false >::on_newindex(shared_ptr& object, const char* name, lua_State *L); + +} } diff --git a/App/script/LuaLibrary.h b/App/script/LuaLibrary.h new file mode 100644 index 0000000..6ad9b1d --- /dev/null +++ b/App/script/LuaLibrary.h @@ -0,0 +1,31 @@ +#pragma once +#include "Lua/LuaBridge.h" + +namespace RBX { +namespace Lua { + + class Library + { + std::string libraryName; + public: + Library(std::string libraryName) + :libraryName(libraryName) + {}; + + const std::string& getLibraryName() const { return libraryName; } + bool operator ==(const Library& other) const + { + return this->libraryName == other.libraryName; + } + }; + // Represents a Reflection::EnumDescriptor::Item in Lua + class LibraryBridge : public Bridge + { + public: + static void registerClassLibrary (lua_State *L); + static int find(lua_State *L, const std::string& libraryName); + static void push(lua_State *L, const Library& item); + static void saveLibraryResult(lua_State *L, int results, std::string libraryName); + }; +} +} diff --git a/App/script/LuaMemory.h b/App/script/LuaMemory.h new file mode 100644 index 0000000..526e4a0 --- /dev/null +++ b/App/script/LuaMemory.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Util/Memory.h" +#include "boost/pool/object_pool.hpp" +#include "boost/iostreams/filter/gzip.hpp" + + +namespace RBX +{ + class LuaAllocator + { + private: + size_t heapSize; + size_t heapCount; + size_t maxHeapSize; + size_t maxHeapCount; + + // memory pools + std::vector*> memPools; + + public: + LuaAllocator(bool usePool = false); + ~LuaAllocator(); + + static size_t heapLimit; // maximum heap size allowed. 0 == no limit + + void clearHeapMax(); + void getHeapStats(size_t& heapSize, size_t& heapCount, size_t& maxHeapSize, size_t& maxHeapCount) const; + void getHeapStats(size_t& heapSize, size_t& heapCount) const; + + bool hasSpace(const long diff); + virtual void* alloc(void *ptr, size_t osize, size_t nsize); + static void * alloc(void *ud, void *ptr, size_t osize, size_t nsize); + + }; +} diff --git a/App/script/LuaSettings.h b/App/script/LuaSettings.h new file mode 100644 index 0000000..d983e90 --- /dev/null +++ b/App/script/LuaSettings.h @@ -0,0 +1,26 @@ +#pragma once + +#include "V8DataModel/GlobalSettings.h" + +namespace RBX +{ + extern const char *const sLuaSettings; + class LuaSettings + : public GlobalAdvancedSettingsItem + { + public: + LuaSettings(); + int gcPause; + int gcStepMul; + double defaultWaitTime; + double smallestWaitTime; + + int gcLimit; //Ideal limit above which we trigger aggressive garbage collection, in average KB per gcFrequency + int gcFrequency; //How many heartbeats between maunal GC steps + + bool areScriptStartsReported; + float waitingThreadsBudget; // 0..1 A percentage + }; + +} + diff --git a/App/script/LuaSignalBridge.cpp b/App/script/LuaSignalBridge.cpp index c03fa17..13c283e 100644 --- a/App/script/LuaSignalBridge.cpp +++ b/App/script/LuaSignalBridge.cpp @@ -27,7 +27,7 @@ template<> int Bridge::on_index(const EventInstance& object, const char* name, lua_State *L) { // The pre-defined "connect()" method - if (strcmp(name, "connect") == 0 || strcmp(name, "Connect") == 0) + if (strcmp(name, "connect")==0) { lua_pushcfunction(L, EventBridge::connect); return 1; diff --git a/App/script/LuaSignalBridge.h b/App/script/LuaSignalBridge.h new file mode 100644 index 0000000..3d7ac17 --- /dev/null +++ b/App/script/LuaSignalBridge.h @@ -0,0 +1,56 @@ +#pragma once + +#include "Lua/LuaBridge.h" +#include "reflection/object.h" +#include "Reflection/Event.h" + +namespace RBX +{ + class Instance; + namespace Lua + { + struct EventInstance + { + const Reflection::EventDescriptor* descriptor; + // We use a weak pointer because references to a Event shouldn't lock the source of the event. + // If the source has been collected, then connecting to the Event will return an empty connection. + weak_ptr source; + + bool operator== (const EventInstance& other) const + { + if (descriptor != other.descriptor) + return false; + + shared_ptr l = source.lock(); + if (!l) + return false; + + shared_ptr l2 = other.source.lock(); + if (!l2) + return false; + + return l == l2; + } + }; + + // specialization + template<> + int Bridge::on_tostring(const EventInstance& object, lua_State *L); + + class EventBridge : public Bridge + { + public: + static int connect(lua_State *L); + + static int wait(lua_State *L); + }; + + class SignalConnectionBridge : public Bridge< rbx::signals::connection > + { + friend class Bridge< rbx::signals::connection >; + static int disconnect(lua_State *L); + }; + } + + +} diff --git a/App/script/LuaSourceContainer.h b/App/script/LuaSourceContainer.h new file mode 100644 index 0000000..78700ee --- /dev/null +++ b/App/script/LuaSourceContainer.h @@ -0,0 +1,74 @@ +#pragma once + +#include "Util/AsyncHttpQueue.h" +#include "Util/ContentId.h" +#include "Util/ProtectedString.h" +#include "V8Tree/Instance.h" + +namespace RBX +{ + +class ContentProvider; + +extern const char* const sLuaSourceContainer; +class LuaSourceContainer + : public DescribedNonCreatable +{ +public: + enum RemoteSourceLoadState + { + NotAttemptedToLoad, + Loaded, + FailedToLoad + }; + + static void loadLinkedScripts(shared_ptr cp, Instance* root, AsyncHttpQueue::ResultJob jobType, boost::function callback); + static void loadLinkedScriptsForInstances(shared_ptr cp, Instances& instances, AsyncHttpQueue::ResultJob jobType, boost::function callback); + static void blockingLoadLinkedScripts(ContentProvider* cp, Instance* root); + static void blockingLoadLinkedScriptsForInstances(ContentProvider* cp, Instances& instances); + static Reflection::RemoteEventDesc event_requestLock; + + LuaSourceContainer(); + + const ContentId& getScriptId() const; + void setScriptId(const ContentId& contentId); + const ProtectedString& getCachedRemoteSource() const; + void setCachedRemoteSource(const ProtectedString& value); + int getCachedRemoteSourceLoadState() const; + void setCachedRemoteSourceLoadState(int value); + Instance* getCurrentEditor() const; + void setCurrentEditor(Instance* newEditor); + virtual void fireSourceChanged() {}; + + rbx::remote_signal requestLock; + rbx::remote_signal lockGrantedOrNot; + +protected: + virtual void onScriptIdChanged() {} + void processRemoteEvent(const Reflection::EventDescriptor& descriptor, const Reflection::EventArguments& args, const SystemAddress& source) override; + +private: + struct LinkedScriptLoadData + { + rbx::atomic scriptCount; + boost::function callbackWhenDone; + shared_ptr context; + AsyncHttpQueue::ResultJob jobType; + + boost::mutex scriptApplyResultClosuresMutex; + std::vector > scriptApplyResultClosures; + }; + + static void linkedSourceCountingVisitor(shared_ptr descendant, int* counter); + static void linkedSourceLoadedHandler(weak_ptr weakScript, AsyncHttpQueue::RequestResult result, + shared_ptr loadedSource, shared_ptr metadata); + static void updateScriptInstancesUnderWriteLock(DataModel* dm, shared_ptr metadata); + static void linkedSourceFetchingVisitor(shared_ptr descendant, shared_ptr cp, + AsyncHttpQueue::ResultJob jobType, shared_ptr metadata); + + ContentId scriptId; + ProtectedString cachedRemoteSource; + RemoteSourceLoadState cachedRemoteSourceLoadState; + weak_ptr currentEditor; +}; +} \ No newline at end of file diff --git a/App/script/LuaVM.h b/App/script/LuaVM.h new file mode 100644 index 0000000..c5cb27f --- /dev/null +++ b/App/script/LuaVM.h @@ -0,0 +1,185 @@ +#pragma once + +#if (defined(_WIN32) || (defined(__APPLE__) && !defined(RBX_PLATFORM_IOS))) && !defined(RBX_STUDIO_BUILD) +#define RBX_SECURE_DOUBLE +#endif + +#ifdef _WIN32 +#define RBX_ALIGN(s) _declspec(align(s)) +#else +#define RBX_ALIGN(s) __attribute__((__aligned__(s))) +#endif + +#include +#include +#if defined(RBX_SECURE_DOUBLE) +#include +#endif + +#ifndef RBX_STUDIO_BUILD +#define LUAVM_SECURE +#endif + +// Utilities for shuffling fields and enum values +#define LUAVM_SHUFFLE_COMMA , + +#ifdef LUAVM_SECURE +#define LUAVM_SHUFFLE2(sep,a0,a1) a1 sep a0 +#define LUAVM_SHUFFLE3(sep,a0,a1,a2) a1 sep a2 sep a0 +#define LUAVM_SHUFFLE4(sep,a0,a1,a2,a3) a3 sep a1 sep a0 sep a2 +#define LUAVM_SHUFFLE5(sep,a0,a1,a2,a3,a4) a4 sep a0 sep a2 sep a1 sep a3 +#define LUAVM_SHUFFLE6(sep,a0,a1,a2,a3,a4,a5) a3 sep a5 sep a2 sep a0 sep a1 sep a4 +#define LUAVM_SHUFFLE7(sep,a0,a1,a2,a3,a4,a5,a6) a2 sep a3 sep a0 sep a4 sep a6 sep a1 sep a5 +#define LUAVM_SHUFFLE8(sep,a0,a1,a2,a3,a4,a5,a6,a7) a7 sep a0 sep a5 sep a6 sep a3 sep a1 sep a2 sep a4 +#define LUAVM_SHUFFLE9(sep,a0,a1,a2,a3,a4,a5,a6,a7,a8) a2 sep a6 sep a4 sep a7 sep a1 sep a8 sep a0 sep a3 sep a5 +#else +#define LUAVM_SHUFFLE2(sep,a0,a1) a0 sep a1 +#define LUAVM_SHUFFLE3(sep,a0,a1,a2) a0 sep a1 sep a2 +#define LUAVM_SHUFFLE4(sep,a0,a1,a2,a3) a0 sep a1 sep a2 sep a3 +#define LUAVM_SHUFFLE5(sep,a0,a1,a2,a3,a4) a0 sep a1 sep a2 sep a3 sep a4 +#define LUAVM_SHUFFLE6(sep,a0,a1,a2,a3,a4,a5) a0 sep a1 sep a2 sep a3 sep a4 sep a5 +#define LUAVM_SHUFFLE7(sep,a0,a1,a2,a3,a4,a5,a6) a0 sep a1 sep a2 sep a3 sep a4 sep a5 sep a6 +#define LUAVM_SHUFFLE8(sep,a0,a1,a2,a3,a4,a5,a6,a7) a0 sep a1 sep a2 sep a3 sep a4 sep a5 sep a6 sep a7 +#define LUAVM_SHUFFLE9(sep,a0,a1,a2,a3,a4,a5,a6,a7,a8) a0 sep a1 sep a2 sep a3 sep a4 sep a5 sep a6 sep a7 sep a8 +#endif + +// Utility class for obfuscating fields of primitive types +// WARNING: this will give incorrect results if T = float. +template class LuaVMValue +{ +public: + operator const T() const + { + #ifdef LUAVM_SECURE + return (T)((uintptr_t)storage + reinterpret_cast(this)); + #else + return storage; + #endif + } + + void operator=(const T& value) + { + #ifdef LUAVM_SECURE + storage = (T)((uintptr_t)value - reinterpret_cast(this)); + #else + storage = value; + #endif + } + + const T operator->() const + { + return operator const T(); + } + +private: + T storage; +}; + +// Encoding/decoding lineinfo +#if defined(LUAVM_SECURE) +#define LUAVM_ENCODELINE(line, pc) ((line) ^ ((pc) << 8)) +#define LUAVM_DECODELINE(line, pc) ((line) ^ ((pc) << 8)) +#else +#define LUAVM_ENCODELINE(line, pc) (line) +#define LUAVM_DECODELINE(line, pc) (line) +#endif + +// Encoding/decoding instructions +#if defined(LUAVM_SECURE) +#define LUAVM_ENCODEINSN(insn, key) ((insn) * key) +#define LUAVM_DECODEINSN(insn, key) ((insn).v * key) +#else +#define LUAVM_ENCODEINSN(insn, key) (insn) +#define LUAVM_DECODEINSN(insn, key) (insn).v +#endif + +typedef unsigned int (*RbxOpEncoder)(unsigned int i, int pc, unsigned key); + +// Utility class +struct lua_State; + +namespace RBX +{ + class ProtectedString; +} + +// Core scripts have a fixed key +// Don't use these except in LuaVM*.cpp! +// These are defines to make sure they don't end up in an executable by complete accident +#define LUAVM_INTERNAL_CORE_ENCODE_KEY 641 +#define LUAVM_INTERNAL_CORE_DECODE_KEY 6700417 + +// Constants for key values +#define LUAVM_KEY_DUMMY 1 +#define LUAVM_KEY_INVALID 0 +#define LUAVM_MODKEY_DUMMY 1 + +namespace LuaVM +{ + // Utilities for working with regular scripts + std::string compile(const std::string& source); + + std::string compileLegacy(const std::string& source); + + int load(lua_State* L, const RBX::ProtectedString& source, const char* chunkname, unsigned int modkey = 1); + + unsigned int getKey(); + + // Utilities for working with core scripts + std::string compileCore(const std::string& source); + + unsigned int getKeyCore(); + unsigned int getModKeyCore(); + + // Controls whether replication uses bytecode or source code + bool useSecureReplication(); + + // Controls whether scripts can be compiled from source code + bool canCompileScripts(); + + // Gets embedded bytecode for core scripts/libraries + std::string getBytecodeCore(const std::string& name); + + //const ref + boost::unordered_map getBytecodeCoreModules(); + + // Old Encoding Scheme + unsigned int rbxOldEncode(unsigned int i, int pc, unsigned int key); + + // Dual-Affine-Xor Encoding + unsigned int rbxDaxEncode(unsigned int i, int pc, unsigned int key); + +} + + +#if defined(RBX_SECURE_DOUBLE) +// Note that users who can find a value can still change magnitude or sign easily. + +// sse2+ only +class LuaSecureDouble +{ +private: + double storage; +public: + static RBX_ALIGN(16) int luaXorMask[4]; + + operator const double() const + { + __m128d xmmKey = _mm_load_pd((double*)(luaXorMask)); + __m128d xmmData = _mm_load_sd(&storage); + __m128d xmmResult = _mm_xor_pd(xmmData, xmmKey ); + return _mm_cvtsd_f64(xmmResult); + } + + void operator=(const double& value) + { + __m128d xmmKey = _mm_load_pd((double*)(luaXorMask)); + __m128d xmmData = _mm_load_sd(&value); + __m128d xmmResult = _mm_xor_pd(xmmData, xmmKey ); + storage = _mm_cvtsd_f64(xmmResult); + } + + static void initDouble(); + +}; +#endif \ No newline at end of file diff --git a/App/script/ModuleScript.h b/App/script/ModuleScript.h new file mode 100644 index 0000000..231074c --- /dev/null +++ b/App/script/ModuleScript.h @@ -0,0 +1,98 @@ +#pragma once + +#include "Reflection/reflection.h" +#include "Script/ThreadRef.h" +#include "Script/LuaSourceContainer.h" +#include "Util/ProtectedString.h" +#include "V8Tree/Instance.h" + +#include + +#include + +namespace RBX +{ + +extern const char* const sModuleScript; +class ModuleScript + : public DescribedCreatable +{ +public: + static const Reflection::PropDescriptor prop_Source; + + enum ScriptSetupState + { + NotRunYet = 0, + Running = 1, + CompletedError = 2, + CompletedSuccess = 3 + }; + + class PerVMState + { + public: + PerVMState(); + virtual ~PerVMState(); + + int getResultRegistryIndex() const; + + // Destroy the current result index and replace it with index. + void reassignResultRegistryIndex(int newIndex); + + void setRunning(boost::intrusive_ptr node); + void setCompletedError(); + void setCompletedSuccess(lua_State* globalStateContainingResult, int resultRegistryIndex); + ScriptSetupState getCurrentState() const; + + void addYieldedImporter(Lua::WeakThreadRef L); + void getAndClearYieldedImporters(std::vector* out); + + void cleanupAndResetState(); + void resetState(); + private: + ScriptSetupState scriptLoadingState; + boost::intrusive_ptr node; + lua_State* globalStateContainingResult; + int resultRegistryIndex; + std::vector yieldedImporters; + + void releaseReferenceIfCompletedSuccessfully(); + void releaseScriptNodeIfPresent(); + }; + + ModuleScript(); + + // Instance + bool askSetParent(const Instance* instance) const override { return true; } + + ProtectedString getSource() const; + void setSource(const ProtectedString& newText); + + std::string requestHash() const; + + PerVMState& vmState(lua_State* vm); + + // Try to get rid of this method once new play button is launched. + static void cleanupAndResetState(const weak_ptr module); + + // Reset the state of the module script without destroying its result index. + void resetState(); + + void setReloadRequested(bool reload) { reloadRequested = reload; } + bool getReloadRequested() const { return reloadRequested; } + + void fireSourceChanged() override; + + rbx::signal starting; + +protected: + void onScriptIdChanged() override; + +private: + ProtectedString source; + bool reloadRequested; + typedef boost::unordered_map VMStateMap; + VMStateMap stateMap; +}; + +} // namespace diff --git a/App/script/ScriptAnalyzer.h b/App/script/ScriptAnalyzer.h new file mode 100644 index 0000000..b83856a --- /dev/null +++ b/App/script/ScriptAnalyzer.h @@ -0,0 +1,122 @@ +#pragma once + +#include +#include + +#include +#include + +struct lua_State; + +namespace RBX +{ + class DataModel; + class Instance; + + namespace ScriptAnalyzer + { + // Don't change codes for the existing warnings - they have a corresponding wiki anchor tag in http://wiki.watrbx.wtf/index.php?title=Script_Analysis + enum WarningCode + { + Warning_Unknown = 0, + + Warning_UnknownGlobal = 1, + Warning_DeprecatedGlobal = 2, + Warning_GlobalUsedAsLocal = 3, + Warning_LocalShadow = 4, + Warning_SameLineStatement = 5, + Warning_MultiLineStatement = 6, + Warning_UnknownType = 7, + Warning_DotCall = 8, + Warning_UnknownMember = 9, + Warning_BuiltinGlobalWrite = 10, + Warning_Placeholder = 11, + + Warning_Internal + }; + + struct Position + { + unsigned int line, column; + + Position(unsigned int line, unsigned int column) + : line(line) + , column(column) + { + } + }; + + struct Location + { + Position begin, end; + + Location() + : begin(0, 0) + , end(0, 0) + { + } + + Location(const Position& begin, const Position& end) + : begin(begin) + , end(end) + { + } + + Location(const Position& begin, unsigned int length) + : begin(begin) + , end(begin.line, begin.column + length) + { + } + + Location(const Location& begin, const Location& end) + : begin(begin.begin) + , end(end.end) + { + } + }; + + struct Error + { + Location location; + std::string text; + }; + + struct Warning + { + WarningCode code; + Location location; + std::string text; + + Warning (WarningCode code, Location location, std::string text) + : code(code) + , location(location) + , text(text) + {} + }; + + struct IntellesenseResult + { + IntellesenseResult() + : name("") + , isLocal(false) + , isFunction(false) + , location(Position(0,0) , Position(0,0)) + {} + + std::string name; + bool isLocal; + bool isFunction; + Location location; + std::vector children; + }; + + struct Result + { + boost::optional error; + std::vector warnings; + std::vector intellesenseAnalysis; + }; + + Result analyze(DataModel* dm, shared_ptr script, const std::string& code); + }; +} diff --git a/App/script/ScriptContext.h b/App/script/ScriptContext.h new file mode 100644 index 0000000..7752982 --- /dev/null +++ b/App/script/ScriptContext.h @@ -0,0 +1,466 @@ +#pragma once + +#include "V8Tree/Service.h" +#include "Util/ProtectedString.h" +#include "util/runstateowner.h" +#include "Script/IScriptFilter.h" +#include "script/ThreadRef.h" +#include "script/ExitHandlers.h" +#include "Security/SecurityContext.h" +#include "Util/AsyncHttpQueue.h" +#include "util/RunningAverage.h" +#include "rbx/RunningAverage.h" + +#define BOOST_DATE_TIME_NO_LIB +#include "boost/date_time/posix_time/posix_time.hpp" + +struct lua_State; +struct lua_Debug; + +LOGGROUP(ScriptContext) +LOGGROUP(ScriptContextRemove) +LOGGROUP(ScriptContextAdd) +LOGGROUP(ScriptContextClose) + +namespace RBX +{ + class LuaSourceContainer; + class LuaAllocator; + class LibraryService; + class ModuleScript; + class ModelInstance; + namespace Stats + { + class Item; + } + namespace Lua + { + class YieldingThreads; + class WeakFunctionRef; + } + namespace Network + { + class Player; + } + class BaseScript; + class CoreScript; + class ScriptStats; + class LuaStatsItem; + + void registerScriptDescriptors(); + + extern const char* const sScriptContext; + class ScriptContext + : public DescribedCreatable + , public Service + , public IScriptFilter + { + friend class LuaStatsItem; + friend class GcJob; + friend class WaitingScriptsJob; + + public: + static const int hookCount; + + struct ScriptStartOptions + { + struct LuaSyntaxError : std::runtime_error + { + LuaSyntaxError(int lineNumber, std::exception& source) + :std::runtime_error(source.what()) + ,lineNumber(lineNumber) + { + } + int lineNumber; + }; + + RBX::Security::Identities identity; + Scripts::Continuations continuations; + boost::function filter; // may throw a LuaSyntaxError + + ScriptStartOptions():identity(RBX::Security::GameScript_) + { + } + }; + + private: + typedef DescribedCreatable Super; + + class ScriptImpersonator : public RBX::Security::Impersonator + { + public: + ScriptImpersonator(lua_State *thread); + }; + + struct GlobalState + { + GlobalState() + : state(0) + , gcCount(0) + { + } + + lua_State* state; + + RunningAverage gcAllocAvg; // average lua memory allocation per luaGcFrequency in KB + int gcCount; + }; + + typedef boost::array GlobalStates; // separate Lua top-level states + GlobalStates globalStates; + Lua::WeakThreadRef commandLineSandbox; + std::set scripts; + RBX::Time nextPendingScripts; + struct ScriptStart + { + shared_ptr script; + ScriptStartOptions options; + }; + std::vector pendingScripts; // scripts waiting to be executed + std::vector loadingScripts; // scripts waiting to be executed + + // An obfuscated pointer to a location near where the object was created. + // copying the object becomes detectable. + // https://en.wikipedia.org/wiki/Feistel_cipher + // the "update" method might be targeted even if it is obfuscated. + struct SecurityAnchor + { + size_t value[2]; + FORCEINLINE void update(const void* ptr) + { +#ifdef _WIN32 + size_t localValue[2] = {reinterpret_cast(ptr), ~reinterpret_cast(ptr)}; + localValue[0] ^= localValue[1]*RBX_BUILDSEED | 20151112; + localValue[1] ^= localValue[0]*20151112 | RBX_BUILDSEED; + value[0] = localValue[0]; + value[1] = localValue[1]; +#endif + } + + FORCEINLINE bool checkAnchor(const void* ptr) const + { +#ifdef _WIN32 + size_t localValue[2] = {value[0], value[1]}; + localValue[1] ^= localValue[0]*20151112 | RBX_BUILDSEED; + localValue[0] ^= localValue[1]*RBX_BUILDSEED | 20151112; + return !((reinterpret_cast(ptr)+localValue[1]) + ^ (~reinterpret_cast(ptr)+localValue[0])); +#else + return true; +#endif + } + }; + SecurityAnchor securityAnchor; + + shared_ptr runService; + + boost::scoped_ptr yieldEvent; // collects all threads that have yielded, and periodically resumes them + + struct WaitingThread + { + Lua::ThreadRef thread; + shared_ptr arguments; + }; + rbx::safe_queue waitingThreads; + + bool robloxPlace; + bool scriptsDisabled; // == don't run the scripts contained in BaseScript objects + bool preventNewConnection; + + shared_ptr statsItem; + bool collectScriptStats; + shared_ptr scriptStats; + std::set > loadedModules; + + int startScriptReentrancy; + + rbx::atomic timedoutCount; + Time::Interval timoutSpan; // The time that is allowed per heartbeat before scripts stop running (0 means no timeouts) + Time timoutTime; // The system time when we should time-out scripts + rbx::atomic timedout; // == scripts should stop running + boost::scoped_ptr timeoutThread; + boost::mutex timeoutMutex; + volatile bool endTimoutThread; + CEvent checkTimeout; + + struct AssetModuleInfo + { + enum State + { + NotFetchedYet = 0, + Fetching, + Fetched, + Failed + }; + State state; + std::vector yieldedImporters; + shared_ptr module; + shared_ptr root; + AssetModuleInfo() + : state(NotFetchedYet) + {} + }; + typedef boost::unordered_map LoadedAssetModules; + LoadedAssetModules loadedAssetModules; + + Time luaGcStartTime; + RunningAverage avgLuaGcInterval; // in msec + RunningAverage avgLuaGcTime; // in msec + + RunningAverageTimeInterval<> resumedThreads; + RunningAverage<> throttlingThreads; // 1 if threads are being deffered + + bool statesClosed; + + public: + ScriptContext(); + virtual ~ScriptContext(); + + /////////////////////////////////////////////////// + // IScriptFilter + /*override*/ virtual bool scriptShouldRun(BaseScript* script); + + static void setAdminScriptPath(const std::string& newPath); + + ////////////////////////////////////////////////// + // Reflection API + static Reflection::BoundProp propScriptsDisabled; + static Reflection::BoundProp propLuaGcLimit; + static Reflection::BoundProp propLuaGcFrequency; + static Reflection::BoundProp propLuaGcStepSize; + void setTimeout(double seconds); + void setCollectScriptStats(bool); + // Core & Starter Scripts + void addStarterScript(int assetId); + void addCoreScript(int assetId, shared_ptr parent, std::string name); + void addCoreScriptLocal(std::string scriptName, shared_ptr parent); + // Experimental error signal for catching errors server-side + rbx::signal)> errorSignal; + // A temporary signals used for diagnostic purposes + rbx::signal, std::string, shared_ptr)> camelCaseViolation; + rbx::signal scriptErrorDetected; + + //////////////////////////////////////////////// + // Configuration + void setRobloxPlace(bool robloxPlace); + void initializeLuaStateSandbox(Lua::WeakThreadRef& threadRef, lua_State* parentState, Security::Identities identity); + void setKeys(unsigned int scriptKey, unsigned int coreScriptModKey); + + //////////////////////////////////////////////// + // Helpers and utilities + Reflection::Variant evaluateStudioCommandItem(const char* itemToEvaluate, shared_ptr script); + static bool checkSyntax(const std::string& code, int& line, std::string& errorMessage); + static lua_State* getGlobalState(lua_State* thread); + static ScriptContext& getContext(lua_State* thread); + static void printCallStack(lua_State* thread, std::string* output = NULL, bool dontPrint = false); + static std::string extractCallStack(lua_State* thread, shared_ptr& source, int& line); + // Shutdown helpers + bool shouldPreventNewConnections() { return preventNewConnection; } + void setPreventNewConnections() { preventNewConnection = true; } + void closeStates(bool resettingSimulation); // Closes down all threads + bool haveStatesClosed() { return statesClosed; } + void cleanupModules(); + + ///////////////////////////////////////////////// + // Script Instance API + // Called by IScriptOwner implementers + void addScript(weak_ptr script, ScriptStartOptions startOptions = ScriptStartOptions()); // checks pointer validity + void removeScript(weak_ptr script); + size_t numScripts() {return scripts.size();} + bool hasScript(BaseScript* script) {return (scripts.find(script) != scripts.end());} + + ///////////////////////////////////////////////// + // Calls that make lua run/resume + void executeInNewThread(RBX::Security::Identities identity, const ProtectedString& script, const char* name); + std::auto_ptr executeInNewThread(RBX::Security::Identities identity, const ProtectedString& script, const char* name, const Reflection::Tuple& arguments); + void executeInNewThreadWithExtraGlobals(RBX::Security::Identities identity, + const ProtectedString& script, const char* name, + const std::map >& extraGlobals); + + // Calls a function + Reflection::Tuple callInNewThread(Lua::WeakFunctionRef& function, const Reflection::Tuple& arguments); + + // Thread-safe call: + void scheduleResume(Lua::ThreadRef thread, shared_ptr arguments); + typedef enum { Success, Yield, Error } Result; + // Resumes the thread. Reports errors and queues yielding threads for later execution + // NOTE: The caller is reponsible for balancing the stack + Result resume(RBX::Lua::ThreadRef thread, int narg); + + ///////////////////////////////////////////// + // Stats + void scriptResumedFromEvent() { resumedThreads.sample(); } + + size_t getThreadCount() const; + shared_ptr getHeapStats(bool clearHighwaterMark); + shared_ptr getScriptStats(); // deprecated. Don't use it anymore + shared_ptr getScriptStatsNew(); + + struct ScriptStat + { + std::string hash; + std::string name; + Instances scripts; + double activity; + unsigned int invocationCount; + }; + void getScriptStatsTyped(std::vector& result); + + double getAvgLuaGcTime() { return avgLuaGcTime.value(); } + double getAvgLuaGcInterval() { return avgLuaGcInterval.value(); } + + void reloadModuleScript(shared_ptr moduleScript); + + bool checkSecurityAnchorValid() const + { + return securityAnchor.checkAnchor(&this->securityAnchor); + } + + protected: + /*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); + private: + rbx::signals::scoped_connection heartbeatConnection; + + boost::scoped_ptr allocator; + + shared_ptr gcJob; + shared_ptr waitingScriptsJob; + void onHeartbeat(const Heartbeat& heartbeat); + void stepGc(); + void resumeWaitingScripts(Time expirationTime); + + static void sandboxThread(lua_State* thread); + static void setThreadIdentityAndSandbox(lua_State* thread, RBX::Security::Identities identity, shared_ptr script); + static RBX::Security::Identities getThreadIdentity(lua_State* thread); + + // Executes a script, throws an std::exception on error + // The script is spawned from the global root thread, but it is "sandboxed" to the extent that global declarations + // don't affect other threads + // If globalStateToExecuteIn is NULL we get the global state to execute in by our current identity, which is the first arg to this function + void executeInNewThread(RBX::Security::Identities identity, const ProtectedString& script, const char* name, + boost::function1 pushArguments, + boost::function2 readImmediateResults, + Scripts::Continuations continuations, + lua_State* globalStateToExecuteIn = NULL, + const std::map >* extraGlobals = NULL, + unsigned int modkey = 1); + + // Resumes the thread (expects the top of the stack to contain a function) + // Throws an std::exception if the thread throws a error + void resumeWithArgs(Lua::ThreadRef thread, shared_ptr arguments); + void resume(Lua::ThreadRef thread, boost::function1 pushArguments, boost::function2 readResults); + + void onChangedScriptEnabled(const Reflection::PropertyDescriptor&); + void onCheckTimeout(); + void onHook(lua_State *L, lua_Debug *ar); + + struct ScriptStatInformation + { + ScriptStatInformation() + {} + std::string name; + Instances scripts; + }; + std::map scriptHashInfo; + + // Functions exposed in the Lua environment: + public: + static void hook(lua_State *L, lua_Debug *ar); + void reportError(lua_State* thread); + + lua_State* getGlobalState(RBX::Security::Identities identity); + + private: + static int print(lua_State *L); + static int doPrint(lua_State *thread, const MessageType& messageType = MESSAGE_OUTPUT); + static int crash(lua_State *L); + static int tick(lua_State* thread); + static int rbxTime(lua_State* thread); + static int time(lua_State* thread); + static int wait(lua_State* thread); + static int delay(lua_State* thread); + static int ypcall(lua_State* thread); + void on_ypcall_success(Lua::WeakThreadRef caller, lua_State* functor); + void on_ypcall_failure(Lua::WeakThreadRef caller, lua_State* functor); + static int spawn(lua_State* thread); + static int printidentity(lua_State* thread); + static int loadfile(lua_State* thread); + static int loadstring(lua_State* thread); + static int notImplemented(lua_State* thread); + static int dofile(lua_State* thread); + static int settings(lua_State* thread); + static int usersettings(lua_State* thread); + static int pluginmanager(lua_State* thread); + static int debuggermanager(lua_State *thread); + static int loadLibrary(lua_State* L); + static int loadRobloxLibrary(lua_State* L); + static int requireModuleScript(lua_State* L); + static int stats(lua_State* thread); + static int version(lua_State* thread); + static int statsitemvalue(lua_State* thread); + + static int requireModuleScriptFromInstance(lua_State* L, shared_ptr moduleScript); + static int requireModuleScriptFromAssetId(lua_State* L, int assetId); + static void moduleContentLoaded(AsyncHttpQueue::RequestResult result, shared_ptr instances, + ScriptContext& sc, Security::Identities identity, lua_State* globalState, AssetModuleInfo* info); + static void moduleContentLinkedSourcesResolved(shared_ptr instances, + shared_ptr foundModuleScript, ScriptContext& sc, Security::Identities identity, + lua_State* globalState, AssetModuleInfo* info); + void startRunningModuleScript(Security::Identities identity, lua_State* globalState, shared_ptr moduleScript); + static void requireModuleScriptSuccessContinuation(shared_ptr moduleScript, + lua_State* threadRunningModuleCode); + static void requireModuleScriptErrorContinuation(shared_ptr moduleScript, + lua_State* threadRunningModuleCode); + + static void reloadModuleScriptInternal(lua_State* globalState, shared_ptr moduleScript); + + static void reloadModuleScriptSuccessContinuation(shared_ptr moduleScript, + lua_State* reloadThread, + int oldResultRegistryIndex); + + static void reloadModuleScriptErrorContinuation(shared_ptr moduleScript, + lua_State* reloadThread); + + static int warn(lua_State *L); + static void validateThreadAccess(lua_State* L); + static int resumeImpl(lua_State* L, int nargs); + + int camelCaseViolationCount; + rbx::signals::connection camelCaseViolationConnection; + void onCamelCaseViolation(shared_ptr object, std::string memberName, shared_ptr script); + + friend class BaseScript; + void disassociateState(BaseScript* script); + + bool openState(size_t idx); + void closeState(lua_State* globalState); + + void startScript(ScriptStart scriptStart); + static void eraseScript(std::vector& container, BaseScript* script); + void startPendingScripts(); + + unsigned int coreScriptModKey; + }; + +#ifdef _DEBUG + class StackBalanceCheck + { + const int oldTop; + lua_State *thread; + bool cancelled; + public: + StackBalanceCheck(lua_State *thread); + ~StackBalanceCheck(); + void cancel() { cancelled = true; } + }; +#define RBXASSERT_BALLANCED_LUA_STACK(L) StackBalanceCheck stackBalanceCheck(L) +#define RBXASSERT_BALLANCED_LUA_STACK2(L) StackBalanceCheck stackBalanceCheck2(L) +#define CANCEL_BALLANCED_LUA_STACK_CHECK() stackBalanceCheck.cancel() +#define CANCEL_BALLANCED_LUA_STACK_CHECK2() stackBalanceCheck2.cancel() +#else +#define RBXASSERT_BALLANCED_LUA_STACK(L) ((void)0) +#define RBXASSERT_BALLANCED_LUA_STACK2(L) ((void)0) +#define CANCEL_BALLANCED_LUA_STACK_CHECK() ((void)0) +#define CANCEL_BALLANCED_LUA_STACK_CHECK2() ((void)0) +#endif +} diff --git a/App/script/ScriptEvent.h b/App/script/ScriptEvent.h new file mode 100644 index 0000000..9e90bfe --- /dev/null +++ b/App/script/ScriptEvent.h @@ -0,0 +1,80 @@ +#pragma once + + +#include "util/runstateowner.h" +#include "g3d/Array.h" +#include "boost/any.hpp" +#include "boost/shared_ptr.hpp" +#include "lua/luabridge.h" +#include "script/threadref.h" +#include + +#include + +struct lua_State; +class ThreadInfo; + + + +namespace RBX { + class Instance; + class ScriptContext; + + namespace Lua { + + class YieldingThreads + { + ScriptContext* context; + + struct WaitingThread + { + boost::intrusive_ptr thread; + RBX::Time waitTime; + RBX::Time resumeTime; + WaitingThread(lua_State *L, RBX::Time::Interval requestedDelay) + :thread(new WeakThreadRef(L)), + waitTime(RBX::Time::now()) + { + resumeTime = waitTime + requestedDelay; + } + + bool operator <(const WaitingThread& other) const + { + return this->resumeTime > other.resumeTime; + } + }; + typedef std::priority_queue< WaitingThread > WaitThreadRefs; + + // Lua refs to threads that are waiting on the event + WaitThreadRefs waitingThreads; + + public: + YieldingThreads(ScriptContext* context); + + // Hooking up consumers: + void queueWaiter(lua_State *L); + void queueWaiter(lua_State *L, LUA_NUMBER delay); + + void resume(double wallTime, Time expirationTime, bool& throttling); + + std::size_t waiterCount() const; + + private: + friend class ScriptContext; + void clearAllSinks(); + }; + + // specialization + template<> + int Bridge::on_tostring(const rbx::signals::connection& object, lua_State *L); + + template<> + int Bridge >::on_tostring(const boost::intrusive_ptr& object, lua_State *L); + + template<> + int Bridge< shared_ptr >::on_tostring(const shared_ptr& object, lua_State *L); + + template<> + int Bridge< shared_ptr >::on_tostring(const shared_ptr& object, lua_State *L); + +} } diff --git a/App/script/ScriptStats.h b/App/script/ScriptStats.h new file mode 100644 index 0000000..4e73ebe --- /dev/null +++ b/App/script/ScriptStats.h @@ -0,0 +1,66 @@ +#pragma once +#include "rbx/RunningAverage.h" +#include "boost/weak_ptr.hpp" +#include "util/Utilities.h" +#include "V8DataModel/Stats.h" +#include "script/ScriptContext.h" +#include +#include + +namespace RBX +{ + +class ScriptStats +{ +public: + struct StatCollection + { + boost::shared_ptr > activity; + boost::shared_ptr > invocations; + }; + typedef std::map ScriptActivityMeterMap; + +protected: + ScriptActivityMeterMap scriptActivityMap; + + std::stack scriptStack; + + void stopCollection(const std::string& scriptHash); + void startCollection(const std::string& scriptHash, bool firstTime); +public: + + ScriptStats(); + + void scriptResumeStarted(const std::string& scriptHash); + void scriptResumeStopped(const std::string& scriptHash); + + const ScriptActivityMeterMap& getScriptActivityMap() const { return scriptActivityMap; } +}; + +class LuaStatsItem : public Stats::Item +{ + ScriptContext* scriptContext; + Stats::Item* averageGcInterval; + Stats::Item* averageGcTime; + Stats::Item* resumedThreads; + Stats::Item* deferredThreads; + +public: + LuaStatsItem(ScriptContext* context) : scriptContext(context) + { + setName("Lua"); + } + + static shared_ptr create(ScriptContext* context) + { + shared_ptr result = Creatable::create(context); + result->init(); + return result; + } + + void init(); + + virtual void update(); +}; + +} \ No newline at end of file diff --git a/App/script/ThreadRef.h b/App/script/ThreadRef.h new file mode 100644 index 0000000..5d6942d --- /dev/null +++ b/App/script/ThreadRef.h @@ -0,0 +1,185 @@ +#pragma once + +#include "rbx/intrusive_ptr_target.h" +#include "boost/intrusive_ptr.hpp" +#include "rbx/boost.hpp" +#include "rbx/threadsafe.h" +#include "reflection/type.h" + +struct lua_State; + +using boost::shared_ptr; + +LOGGROUP(WeakThreadRef) + +namespace RBX { + + namespace Lua { + + void dumpThreadRefCounts(); + + // Used internally + namespace detail { + class LiveThreadRef + : public rbx::quick_intrusive_ptr_target + , public Diagnostics::Countable + , boost::noncopyable + { + lua_State* L; + int threadId; + friend class WeakThreadRef; + friend class ThreadRef; + public: + // Do not create this. It is an internal class + LiveThreadRef (lua_State* thread); + ~LiveThreadRef(); + bool empty() const { + return L == NULL; + } + lua_State* thread() const { + return L; + } + + }; + } + + // You get this by calling lock() on WeakThreadRef + class ThreadRef + : public Diagnostics::Countable + { + boost::intrusive_ptr liveThreadRef; + friend class WeakThreadRef; + ThreadRef (detail::LiveThreadRef* liveThreadRef):liveThreadRef(liveThreadRef) {} + public: + ThreadRef() {} + ThreadRef(lua_State* thread) + :liveThreadRef(new detail::LiveThreadRef(thread)) {} + lua_State* get() const { + return liveThreadRef ? liveThreadRef->thread() : NULL; + } + operator lua_State*() const + { + return get(); + } + bool empty() const { + return liveThreadRef && liveThreadRef->thread() != NULL; + } + }; + + // Registers a weak reference to a thread, ensuring that it isn't collected (sometimes) + class WeakThreadRef + : public rbx::quick_intrusive_ptr_target + , boost::noncopyable + , public Diagnostics::Countable + { + // TODO: boost::mutex would be safer + typedef rbx::spin_mutex Mutex; + static Mutex sync; + public: + class Node + : public rbx::quick_intrusive_ptr_target + , boost::noncopyable + { + friend class WeakThreadRef; + WeakThreadRef* first; + public: + Node():first(0) {} + ~Node(); + static boost::intrusive_ptr create(lua_State* thread); + static Node* get(lua_State* thread); + + // Clear all refs to thread and its children + void eraseAllRefs(); + + template + void forEachRefs(Func func) + { + for (WeakThreadRef* ref = first; ref!=NULL; ref = ref->next) + { + func(ref->lock()); + } + } + }; + friend class Node; + private: + WeakThreadRef* previous; + WeakThreadRef* next; + boost::intrusive_ptr liveThreadRef; + void addRef(lua_State* L); + void addToNode(); + void removeFromNode(); + protected: + Node* node; + virtual void removeRef(); + lua_State* thread() const { + return threadDangerous(); + } + public: + WeakThreadRef():node(0), previous(0), next(0) {} + WeakThreadRef(lua_State* thread); + WeakThreadRef(const WeakThreadRef& other); + WeakThreadRef& operator=(const WeakThreadRef& other); + virtual ~WeakThreadRef(); + + bool operator==(const WeakThreadRef& other) const; + bool operator!=(const WeakThreadRef& other) const; + void reset(); + bool empty() const { + return liveThreadRef ? liveThreadRef->thread()==0 : true; + } + ThreadRef lock() + { + return ThreadRef(liveThreadRef.get()); + } + lua_State* threadDangerous() const { + return liveThreadRef ? liveThreadRef->thread() : NULL; + } + + }; + + // A function that takes any number of arguments and returns a tuple + typedef boost::function(shared_ptr)> GenericFunction; + + class IAsyncResult + { + public: + // This may throw + virtual boost::shared_ptr getValue() = 0; + virtual ~IAsyncResult() {} + }; + // A function that takes any number of arguments and returns the result through a callback + typedef boost::function, boost::function)> GenericAsyncFunction; + + class WeakFunctionRef : public WeakThreadRef + { + private: + int functionId; + typedef WeakThreadRef Super; + public: + WeakFunctionRef():functionId(0) {} + + WeakFunctionRef(lua_State* thread, int index); // Constructs a FunctionRef from the Lua stack + + virtual ~WeakFunctionRef(); + + // Copy: + WeakFunctionRef(const WeakFunctionRef& other); + WeakFunctionRef& operator=(const WeakFunctionRef& other); + + // Query: + bool operator==(const WeakFunctionRef& other) const; + bool operator!=(const WeakFunctionRef& other) const; + + friend WeakFunctionRef lua_tofunction(lua_State* L); + friend void lua_pushfunction(lua_State* L, const WeakFunctionRef& function); + protected: + virtual void removeRef(); + }; + + // Operations with Lua + WeakFunctionRef lua_tofunction(lua_State* L, int index); + void lua_pushfunction(lua_State* L, const WeakFunctionRef& function); + void lua_pushfunction(lua_State* L, shared_ptr function); + void lua_pushfunction(lua_State* L, shared_ptr function); + +} } diff --git a/App/script/script.h b/App/script/script.h new file mode 100644 index 0000000..6e8d110 --- /dev/null +++ b/App/script/script.h @@ -0,0 +1,192 @@ +#pragma once + +#include "V8Tree/Instance.h" +#include "Script/ThreadRef.h" +#include "Util/ScriptInformationProvider.h" +#include "Util/ProtectedString.h" +#include "script/LuaSourceContainer.h" +#include "rbx/atomic.h" +#include +#include + +struct lua_State; + +namespace RBX +{ + class IScriptOwner; + class ScriptContext; + class RuntimeScriptService; + class ScriptInformationProvider; + class ContentProvider; + + namespace Network + { + class Player; + } + + typedef ContentId ScriptId; + extern const char* const sBaseScript; + class BaseScript + : public DescribedNonCreatable + { + private: + typedef DescribedNonCreatable Super; + + public: + class Slot; + + // Used for development only. It allows you to load CoreScripts from your local disk + static std::string adminScriptsPath; + static bool hasCoreScriptReplacements(); + + void restartScript(); + protected: + RuntimeScriptService* workspace; + + /////////////////////////////////// + // Instance + /*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); + /*override*/ void onAncestorChanged(const AncestorChanged& event); + /*override*/ void onScriptIdChanged(); + + private: + static const std::string emptyString; + + weak_ptr localPlayer; + + bool disabled; + bool badLinkedScript; + + RuntimeScriptService* computeNewWorkspace(); + + public: + struct Code + { + bool loaded; + boost::flyweight script; + + Code() + :loaded(false) + {} + Code(const boost::flyweight& script) + :loaded(true) + ,script(script) + {} + }; + BaseScript(); + ~BaseScript(); + + + static const Reflection::PropDescriptor prop_SourceCodeId; + + weak_ptr getLocalPlayer() { return localPlayer; } + void setLocalPlayer(const shared_ptr& localPlayer) { this->localPlayer = localPlayer; } + + // Thread management + boost::intrusive_ptr threadNode; + rbx::signal starting; + rbx::signal stopped; + + bool isDisabled() const { return disabled; } + static const Reflection::PropDescriptor prop_Disabled; + + virtual Code requestCode(ScriptInformationProvider* scriptInfoProvider=NULL); + + virtual void extraErrorReporting(lua_State *thread) {} + + //Properties + bool getDisabled() const { return disabled; } + void setDisabled(bool value); + + virtual const std::string& requestHash() const; + }; + // A BaseScript is started when a containing IScriptOwner sends it to the ScriptContext service + extern const char* const sScript; + class Script + : public DescribedCreatable + { + private: + typedef DescribedCreatable Super; + + private: + boost::flyweight embeddedSource; + std::string embeddedSourceHash; + + public: + Script(); + ~Script(); + + static const Reflection::PropDescriptor prop_EmbeddedSourceCode; + + /*override*/ XmlElement* writeXml(const boost::function& isInScope, RBX::CreatorRole creatorRole) + { + return Super::writeXml(isInScope, creatorRole); + } + + /*override*/ bool askSetParent(const Instance* instance) const + { + // Scripts can be anywhere + return true; + } + + bool isCodeEmbedded() const { return getScriptId().isNull(); } + + /*override*/ Code requestCode(ScriptInformationProvider* scriptInfoProvider=NULL); + + /*override*/ const std::string& requestHash() const; + + void setEmbeddedCode(const ProtectedString& value); + const boost::flyweight& getEmbeddedCode() const; + const ProtectedString& getEmbeddedCodeSafe() const; + /*override*/ int getPersistentDataCost() const; + /*override*/ void fireSourceChanged(); + + private: + std::string getHash() { return requestHash(); } + + static const Reflection::BoundFuncDesc func_GetHash; + }; + + // Only runs on a local machine if either + // a) Inside a tool which is inside a local character + // b) Inside the local backpack + + // Local scripts have the full power of a normal script, but can also interact with the Mouse. + // While they are currently run client side, this is a large security hole that will have to be addressed. + // The plan is to have them execute server side, but with an adapter taking the place of the "Mouse" object and abstracting it per-user + // + // A better name would be GuiScript or UserScript, if we could redo this work. + extern const char* const sLocalScript; + class LocalScript + : public DescribedCreatable + { + public: + + LocalScript(); + ~LocalScript() {} + }; + + class BaseScript::Slot + { + rbx::signals::connection connection; + public: + // A Slot must keep a reference to its connection, because it + // must be capable of disconnecting itself. + void assignConnection(const rbx::signals::connection& connection) + { + this->connection = connection; + } + protected: + Slot() + { + } + ~Slot() + { + // TODO: Disconnect here??? + } + void disconnect() + { + connection.disconnect(); + } + }; +} diff --git a/App/security/ApiSecurity.h b/App/security/ApiSecurity.h new file mode 100644 index 0000000..de94f0f --- /dev/null +++ b/App/security/ApiSecurity.h @@ -0,0 +1,377 @@ +#pragma once +#include +#include "Security/FuzzyTokens.h" +#include "Security/RandomConstant.h" + +#if defined(RBX_PLATFORM_DURANGO) +#define NOINLINE __declspec(noinline) +#elif defined(_WIN32) +#include +#include +#undef min + +#define FORCEINLINE __forceinline +#define NOINLINE __declspec(noinline) + +// All of the .text +namespace RBX{ namespace Security { + extern volatile const uintptr_t rbxTextBase; + extern volatile const size_t rbxTextSize; + extern volatile const uintptr_t rbxTextEndNeg; + extern volatile const size_t rbxTextSizeNeg; + extern volatile const uintptr_t rbxVmpBase; + extern volatile const size_t rbxVmpSize; +} +} + + +#else +#define FORCEINLINE inline __attribute__((always_inline)) +#define NOINLINE __attribute__((noinline)) +#endif + + +// This file defines some secure caller function to prevent key functions +// from being called from a dll. +// +// For many functions, this is actually intractable. With just a return address +// check, someone can simply push the location of a "C3" byte (ret) in our code +// and then jump to our function. The return address will appear to be in our code. +// +// When the check becomes stronger, it is a simple matter to find the target +// function called from another function in our code. From this point it is easy +// to call (into) that function. Even if the result isn't directly returned, it +// will typically be on the stack. +// +// callbacks are problematic. On one hand, we can check callbacks before setting +// and before calling them. However, callbacks could be set to "CC" bytes (int 3) in +// our code, which would throw an exception that could be handled. +// +// Secure calling in this file almost certainly will not work within a VM protected +// section as the registers and stack will be nonstandard. +// +// It will be trivial to find where these checks are done as well, as they all +// modify the same global values. + +namespace RBX +{ +FORCEINLINE static bool isRbxTextAddr(const void* const ptr) +{ +#if defined(_WIN32) && !defined(RBX_PLATFORM_DURANGO) && !defined(RBX_STUDIO_BUILD) + return (reinterpret_cast(ptr) - RBX::Security::rbxTextBase < RBX::Security::rbxTextSize); +#else + return true; +#endif +} + +template FORCEINLINE void callCheckSetBasicFlag(unsigned int flags) +{ + Tokens::sendStatsToken.addFlagFast(value); + Tokens::simpleToken |= value; +} + +// 33222222222 2 1 111 11111100 000 0 0 000 +// 10987654321 0 9 876 54321098 765 4 3 210 +// rsvd cpy pi veh ntapi cs locked name +static const unsigned int kNameApiOffset = 0; +static const unsigned int kRbxLockedApiOffset = 3; +static const unsigned int kChangeStateApiOffset = 4; +static const unsigned int kNtApiNoNtdll = (1<<8); +static const unsigned int kNtApiNoText = (1<<9); +static const unsigned int kNtApiNoApi = (1<<10); +static const unsigned int kNtApiNoSyscall = (1<<11); +static const unsigned int kNtApiNoTemplate = (1<<12); +static const unsigned int kNtApiEarly = (1<<13); +static const unsigned int kNtApiHash = (1<<14); +static const unsigned int kNtApiNoCall = (1<<15); +static const unsigned int kVehWpmFail = (1<<16); +static const unsigned int kVehPrologFail = (1<<17); +static const unsigned int kVehNoNtdll = (1<<18); +static const unsigned int kPingItem = (1<<19); +static const unsigned int kScriptContextCopy = (1<<20); +static const unsigned int kLuaHooked = (1<<21); + + +template FORCEINLINE void callCheckSetApiFlag(unsigned int flags) +{ + Tokens::apiToken.addFlagSafe(flags << offset); +} + +FORCEINLINE void callCheckNop(unsigned int flags) +{ +} + +// This checks that the return address: +// 1) is within the .text section +// 2) calling instruction has a correct relative offset. (must be call imm32) +// 3) caller of calling function is within our .text section +// Disabled in NoOpt because this won't be inlined. +static const int kCallCheckCodeOnly = 1; +static const int kCallCheckCallArg = 2; +static const int kCallCheckCallersCode = 3; +static const int kCallCheckRegCall = 4; + +template +FORCEINLINE static unsigned int checkRbxCaller(const void* const funcAddress) +{ +#if defined(_WIN32) && !defined(_NOOPT) && !defined(LOVE_ALL_ACCESS) && !defined(RBX_STUDIO_BUILD) && !defined(RBX_PLATFORM_DURANGO) + unsigned int flags = 0; + + void* returnAddress = _ReturnAddress(); + flags |= isRbxTextAddr(returnAddress) ? 0 : (1<<0); + if (!flags) + { + switch(level) + { + case kCallCheckCallersCode: + { + void* aora = _AddressOfReturnAddress(); + void* secondReturnAddress = (*((void***)(aora)-1))[1]; + flags |= isRbxTextAddr(secondReturnAddress) ? 0 : (1<<2); + } + // continue + case kCallCheckCallArg: + { + unsigned int offset = *((unsigned int*)(returnAddress) - 1); + flags |= (offset == ((unsigned int)(funcAddress) + - (unsigned int)(returnAddress))) ? 0 : (1<<1); + } + break; + case kCallCheckRegCall: + { + // this is a weaker check. call r32 is a prefixed 2 byte instruction. + // because the register may have changed before this is called, there + // isn't much point in checking it. + static const unsigned short kX86RegCall = 0xD7FF; // due to little endian + unsigned short opcode = *((unsigned short*)(returnAddress) - 1); + flags |= ((opcode|0x0700) == kX86RegCall) ? 0 : (1<<3); + } + break; + default: + break; + } + } + if (flags) + { + action(flags); + } + return flags; +#else + return 0; +#endif +} + +namespace Security{ + static const unsigned int kCheckDefault = 0; + static const unsigned int kCheckReturnAddr = 1; + static const unsigned int kCheckNoThreadInit = 2; + static const unsigned int kAllowVmpAll = 4; // I'm not sure if I only need to look at mutant, mutant+plain, etc... +} + +// Only supporting ntdll for now. +#if defined(_WIN32) && !defined(RBX_PLATFORM_DURANGO) + +inline const WCHAR* getUnicodeDllName(const UNICODE_STRING& str) +{ + USHORT idx; + for (idx = str.Length/sizeof(WCHAR); idx != 0; --idx) + { + if (str.Buffer[idx] == L'\\') + { + break; + } + } + return str.Buffer + idx + 1; +} + +// This gets the location of ntdll using the documented parts of the PE format. +// The intent is to make it harder for anyone who wants to hook GetModuleHandle. +// This is not foolproof as the PEB or PEB pointer could be modified. +inline HMODULE rbxGetNtdll() +{ + const WCHAR* kNtDll = L"ntdll.dll"; + PEB* peb = reinterpret_cast(__readfsdword(0x30)); + PEB_LDR_DATA* pebLdrData = peb->Ldr; + LIST_ENTRY imoListBase = pebLdrData->InMemoryOrderModuleList; + LIST_ENTRY* imoListWalk = &imoListBase; + int moduleLimit = 256; + while ((imoListWalk->Flink != imoListBase.Blink) && --moduleLimit) + { + imoListWalk = imoListWalk->Flink; + LDR_DATA_TABLE_ENTRY* thisPe = reinterpret_cast(reinterpret_cast(imoListWalk) - 8); + const WCHAR* dllName = getUnicodeDllName(thisPe->FullDllName); + const int dllNameLen = (thisPe->FullDllName.Length - (dllName - thisPe->FullDllName.Buffer))/sizeof(WCHAR); + + //filter here + unsigned int dest = ((unsigned int)(kNtDll) + RBX_BUILDSEED); + volatile unsigned int tmp = dest; + const WCHAR* const ntdllName = (const WCHAR* const)(tmp - RBX_BUILDSEED); + if (_wcsnicmp(dllName, ntdllName, std::min(dllNameLen, 9)) == 0) + { + return reinterpret_cast(thisPe->DllBase); + } + } + return 0; +} + + +// Find the location of the function matched by "filter" within ntdll's export table. +// Doesn't directly expose the name of the function that has been matched. +inline void* rbxNtdllProcAddress(HMODULE module, bool filter(const char*) ) +{ + DWORD* loc = 0; + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)module; + PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + (char *)pDosHeader); + IMAGE_DATA_DIRECTORY* imageDataDir = pNTHeader->OptionalHeader.DataDirectory; + DWORD exportVa = imageDataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + DWORD base = reinterpret_cast(module); + PIMAGE_EXPORT_DIRECTORY pExport = reinterpret_cast(base + exportVa); + DWORD* pAddressOfNames = reinterpret_cast(pExport->AddressOfNames + base); + DWORD* pAddressOfFuncs = reinterpret_cast(pExport->AddressOfFunctions + base); + WORD* pAddressOfOrds = reinterpret_cast (pExport->AddressOfNameOrdinals + base); + for (DWORD i = 0; i < pExport->NumberOfNames; ++i) + { + if (filter(reinterpret_cast(base + pAddressOfNames[i]))) + { + return reinterpret_cast(base + pAddressOfFuncs[pAddressOfOrds[i]]); + } + } + return loc; +} +#endif + + +struct CallChainInfo +{ + uint32_t handler; + uint32_t ret; + CallChainInfo() : handler(0), ret(0) {} + CallChainInfo(uint32_t handler, uint32_t ret) : handler(handler), ret(ret) {} +}; + +#if defined(_WIN32) && !defined(RBX_STUDIO_BUILD) && !defined(RBX_RCC_SECURITY) && !defined(RBX_PLATFORM_DURANGO) +// Call Stack, function with: +// +// == C++ Exceptions == | == SEH3 Exceptions == +// +1C locals | +20 locals +// ... | ... +// +10 GS Cookie | +10 *nextHandler() +// + C *nextHandler | + C *handler() +// + 8 *handler() | + 8 scopeTable +// + 4 state | + 4 tryLevel +// +00 saved ebp | +00 saved ebp +// - 4 return address | - 4 return address +// - 8 first arg | - 8 first arg +// -xx last arg | -xx last arg +// +// This will get generated in some functions that don't have try-catch blocks. Basically +// if something could throw, objects might need to be destroyed. +// +// fs:[0] also contains a pointer to nextHandler, but is out of place in the middle of a +// function. +// +// The list will always have a size of at least 2 -- the entry in ntdll and the entry we +// started with. +// +// There is a case with boost::threads, as these get created with msvcr's threadstartex +// (after ntdll rtlInitThread). However, boost::threads will have three exception handlers +// in the boost code before getting into msvcr. + +template FORCEINLINE uint32_t detectDllByExceptionChain(void* addrOfChain, unsigned int kFlags) +{ + static const size_t kStackNextIdx = 0; + static const size_t kStackHandlerIdx = 1; + static const size_t kStackReturnIdx = 4; + static const size_t kStackEndingArgIdx = 6; + DWORD* stkPtr = reinterpret_cast(addrOfChain); + uintptr_t textEndNeg = RBX::Security::rbxTextEndNeg; + size_t textSizeNeg = RBX::Security::rbxTextSizeNeg; + uintptr_t vmpBase = RBX::Security::rbxVmpBase; + size_t vmpSize = RBX::Security::rbxVmpSize; + uint32_t result = 0; + for (size_t i = 0; i < kMaxDepth; ++i) + { + // check for end of chain in ntdll. + if (!(kFlags & Security::kCheckNoThreadInit) && (stkPtr[0] == 0xFFFFFFFF)) + { + // check if handler chain init in roblox. (equivalent to "addr - base >= size") + if ((stkPtr[kStackEndingArgIdx] + textEndNeg ) <= textSizeNeg) + { + result |= 1 << (i*3); + } + break; + } + // check if handler in roblox code. (equivalent to "addr - base >= size") + if ((stkPtr[kStackHandlerIdx] + textEndNeg ) <= textSizeNeg) + { + result |= 1 << (i*3 + 1); + break; + } + if ( (kFlags & Security::kCheckReturnAddr) // check return enabled + && (stkPtr[kStackReturnIdx] + textEndNeg ) <= textSizeNeg) // not in .text + { + if (!((kFlags&Security::kAllowVmpAll) // allow vmp + && (stkPtr[kStackReturnIdx] - vmpBase ) > vmpSize)) // not in vmp + { + result |= 1 << (i*3 + 2); + break; + } + } + // follow next + stkPtr = reinterpret_cast(stkPtr[kStackNextIdx]); + } + return result; +} + +template FORCEINLINE void generateCallInfo(void* addrOfFirstArg, std::vector& info) +{ + static const size_t kStackNextIdx = 0; + static const size_t kStackHandlerIdx = 1; + static const size_t kStackReturnIdx = 4; + static const size_t kStackEndingArgIdx = 6; + static const size_t kStackFirstArgToNextHandler = 5; + DWORD* stkPtr = reinterpret_cast(addrOfFirstArg) - kStackFirstArgToNextHandler; + for (size_t i = 0; i < kMaxDepth; ++i) + { + info.push_back(CallChainInfo(stkPtr[kStackHandlerIdx],stkPtr[kStackReturnIdx])); + // check for end of chain in ntdll. + if (stkPtr[0] == 0xFFFFFFFF) + { + break; + } + // follow next + stkPtr = reinterpret_cast(stkPtr[kStackNextIdx]); + } +} + +template FORCEINLINE uint32_t detectDllByExceptionChainStack(void* addrOfFirstArg, unsigned int kFlags) +{ + static const size_t kStackFirstArgToNextHandler = 5; + DWORD* stkPtr = reinterpret_cast(addrOfFirstArg) - kStackFirstArgToNextHandler; + return detectDllByExceptionChain(stkPtr, kFlags); +} + +// not all functions add an exception handler to the chain, so the stack based method doesn't +// always work. +template FORCEINLINE uint32_t detectDllByExceptionChainTeb(unsigned int kFlags) +{ + return detectDllByExceptionChain(reinterpret_cast(__readfsdword(0)), kFlags ); +} +#else +template FORCEINLINE uint32_t detectDllByExceptionChainTeb(unsigned int kFlags) +{ + return 0; +} +template FORCEINLINE uint32_t detectDllByExceptionChainStack(void* addrOfFirstArg, unsigned int kFlags) +{ + return 0; +} + +template FORCEINLINE void generateCallInfo(void* addrOfFirstArg, std::vector& info) +{ + return; +} + +#endif + +} diff --git a/App/security/FuzzyTokens.h b/App/security/FuzzyTokens.h new file mode 100644 index 0000000..3aa648b --- /dev/null +++ b/App/security/FuzzyTokens.h @@ -0,0 +1,86 @@ +#pragma once +#include "boost/thread/mutex.hpp" + +namespace RBX +{ + // This is designed to be an anti-tamper security reporting mechanism. + // Add flag is based on the number of bits in the flag, and succeeds + // with probability 1 - 0.5**N per transmitted packet. This might + // seem like an issue, but most of the checks will re-trigger an addition + // meaning an exploiter is unlikely to be in the game for more than 30 + // seconds. Attempts to modify the packet are very likely to fail. + + namespace Security + { + unsigned long long teaDecrypt(unsigned long long inTag); + unsigned long long teaEncrypt(unsigned long long inTag); + } + + namespace Tokens + { + union binaryTag + { + unsigned int asHalf[2]; + unsigned long long asFull; + }; + } + + class ClientFuzzySecurityToken + { + Tokens::binaryTag tag; + Tokens::binaryTag prevTag; + Tokens::binaryTag savedTag; + boost::mutex tokenMutex; + public: + ClientFuzzySecurityToken(unsigned long long inTag); + void set(unsigned long long inTag); + +#ifdef WIN32 + __forceinline +#else + inline +#endif + void addFlagFast(unsigned long long flags) + { + tag.asFull |= flags; + } + +#ifdef WIN32 + __forceinline +#else + inline +#endif + void addFlagSafe(unsigned long long flags) + { + boost::mutex::scoped_lock lock(tokenMutex); + tag.asFull |= flags; + } + unsigned long long crypt(); + unsigned long long getPrev() + { + return prevTag.asFull; + } + }; + + // The server can check this. Each '1' transmitted from the client has + // a 50% chance of being a '0' here, thus it it fuzzy. + class ServerFuzzySecurityToken + { + Tokens::binaryTag lastTag; + Tokens::binaryTag tag; + unsigned int ignoreFlags; + public: + ServerFuzzySecurityToken(unsigned long long inTag, unsigned int ignoreFlags = 0); + void setLastTag(unsigned long long tag) { lastTag.asFull = tag;} + unsigned long long decrypt(unsigned long long inTag); + }; + + namespace Tokens + { + extern ClientFuzzySecurityToken sendStatsToken; + extern unsigned int simpleToken; + extern ClientFuzzySecurityToken apiToken; + } + +} + diff --git a/App/security/JunkCode.h b/App/security/JunkCode.h new file mode 100644 index 0000000..9d46365 --- /dev/null +++ b/App/security/JunkCode.h @@ -0,0 +1,170 @@ +#pragma once + +#if defined(_WIN32) && !defined(RBX_STUDIO_BUILD) && !defined(RBX_PLATFORM_DURANGO) +#include "Security/RandomConstant.h" + +// This is junk code generation. All of this is intended to be inlined +// and generally do nothing. + +// there are 15*17 different seeds from this. +// (RBX_BUILDSEED%15 + 1) is between 1 and 16, and is coprime to 17. +#define RBX_JUNK (junk< ((RBX_BUILDSEED%15 + 1)*__LINE__ + RBX_BUILDSEED) % 17>()) + +template inline void junk() {} + +#define RBX_NOP0 __asm _emit 0x0f __asm _emit 0x1f __asm _emit 0x00 +#define RBX_NOP1 __asm _emit 0x8d __asm _emit 0x76 __asm _emit 0x00 +#define RBX_NOP2 __asm _emit 0x0f __asm _emit 0x1f __asm _emit 0x40 __asm _emit 0x00 +#define RBX_NOP3 __asm _emit 0x8d __asm _emit 0x74 __asm _emit 0x26 __asm _emit 0x00 +#define RBX_NOP4 __asm _emit 0x90 __asm _emit 0x8d __asm _emit 0x74 __asm _emit 0x26 __asm _emit 0x00 +#define RBX_NOP5 __asm _emit 0x66 __asm _emit 0x0f __asm _emit 0x1f __asm _emit 0x44 __asm _emit 0x00 __asm _emit 0x00 +#define RBX_NOP6 __asm _emit 0x8d __asm _emit 0xb6 __asm _emit 0x00 __asm _emit 0x00 __asm _emit 0x00 __asm _emit 0x00 +template<> inline void junk<0>() +{ + _asm + { + RBX_NOP0; + } +} + + +template<> inline void junk<1>() +{ + _asm + { + RBX_NOP1; + } +} + +template<> inline void junk<2>() +{ + _asm + { + RBX_NOP2; + } +} + +template<> inline void junk<3>() +{ + _asm + { + RBX_NOP3; + } +} + +template<> inline void junk<4>() +{ + _asm + { + RBX_NOP4; + } +} + +template<> inline void junk<5>() +{ + _asm + { + RBX_NOP5; + } +} + +template<> inline void junk<6>() +{ + _asm + { + RBX_NOP6; + } +} + +template<> inline void junk<7>() +{ + _asm + { + RBX_NOP0; + RBX_NOP1; + } +} + +template<> inline void junk<8>() +{ + _asm + { + RBX_NOP1; + RBX_NOP2; + } +} + +template<> inline void junk<9>() +{ + _asm + { + RBX_NOP1; + RBX_NOP3; + } +} + +template<> inline void junk<10>() +{ + _asm + { + RBX_NOP4; + RBX_NOP0; + } +} + +template<> inline void junk<11>() +{ + _asm + { + RBX_NOP3; + RBX_NOP5; + } +} + +template<> inline void junk<12>() +{ + _asm + { + RBX_NOP2; + RBX_NOP5; + } +} + +template<> inline void junk<13>() +{ + _asm + { + RBX_NOP6; + RBX_NOP0; + } +} + +template<> inline void junk<14>() +{ + _asm + { + RBX_NOP2; + RBX_NOP1; + } +} + +template<> inline void junk<15>() +{ + _asm + { + RBX_NOP4; + RBX_NOP5; + } +} + +template<> inline void junk<16>() +{ + _asm + { + RBX_NOP3; + RBX_NOP2; + } +} +#else +#define RBX_JUNK +#endif diff --git a/App/security/RandomConstant.h b/App/security/RandomConstant.h new file mode 100644 index 0000000..8fde7e0 --- /dev/null +++ b/App/security/RandomConstant.h @@ -0,0 +1,2 @@ +#pragma once +#define RBX_BUILDSEED 3942749 diff --git a/App/security/SecurityContext.h b/App/security/SecurityContext.h new file mode 100644 index 0000000..3a04a0c --- /dev/null +++ b/App/security/SecurityContext.h @@ -0,0 +1,117 @@ + +#pragma once +#include "rbxformat.h" +#include "rbx/boost.hpp" +#include "g3d/format.h" + +namespace boost +{ + template class thread_specific_ptr; +} + +namespace RBX +{ + namespace Security + { + typedef enum { + Anonymous = 0, + LocalGUI_, // Any action initiated by Roblox Studio or the mouse + GameScript_, // Execution of a BaseScript object inside any DataModel + GameScriptInRobloxPlace_, // Execution of a BaseScript object inside any DataModel, if the place was authored by Roblox + RobloxGameScript_, // Execution of a BaseScript object written by Roblox inside any DataModel + CmdLine_, // Any script executed from the Studio command line +#if defined(RBX_STUDIO_BUILD) + StudioPlugin, // Any Studio plug-in script +#endif + COM, // Scripts executed via the COM API (usually comes from watrbx.wtf) + WebService, // Scripts executed via the Web Service API (usually comes from watrbx.wtf) + Replicator_, // Receiving data via replication + COUNT_Identities // Not a true identity. Used for enumeration + } Identities; + + typedef enum { + None =0, // Any identity can access this feature, including in-game scripts + Plugin =1, // Second-lowest access level, just above in-game script + RobloxPlace =2, // A Roblox place that we own. Therefore scripts are more trusted and we allow + // preliminary features + LocalUser =3, // non-game permission. Usually for IDE + WritePlayer =4, // Permissions for changing player name, userId, etc. + RobloxScript =5, // A script, such as a CoreScript, that we run inside a game + Roblox =6, // Highest level of permission + +#ifdef RBX_TEST_BUILD + TestLocalUser =None, //For exposing Lua functions to the ReleaseTest build +#else + TestLocalUser =LocalUser, +#endif + } Permissions; + + // different classes of VM that derive from the permission level + typedef enum { + VM_Default = 0, // most scripts go here +#if defined(RBX_STUDIO_BUILD) + VM_StudioPlugin, // Sandbox for studio plugin scripts +#endif + VM_RobloxScriptPlus, // scripts with the permission level of RobloxScript or higher go here + COUNT_VM_Classes + } VMClasses; + + class Impersonator; + + class Context + { + friend class Impersonator; + + public: + const Identities identity; + static Context& current(); + + // Throws an exception if the current thread's Context doesn't have the requested Role + void requirePermission(Permissions permission, const char* operation = 0) const + { + if (!isInRole(identity, permission)) { +#ifndef _DEBUG + // obfuscate error string + // TODO: Can we obfuscate the code without obfuscating the error? + // Daniel: NO + //if (operation) + // throw RBX::runtime_error("s %s", operation); + //else + // throw RBX::runtime_error("s"); + throw std::runtime_error(""); +#else + if (operation) { + throw RBX::runtime_error("The current identity (%d) cannot %s (requires %d)", identity, operation, permission); + } else { + throw RBX::runtime_error("The current identity (%d) cannot perform the requested operation (requires %d)", identity, permission); + } +#endif + } + } + + bool hasPermission(Permissions permission) + { + return isInRole(identity, permission); + } + + static bool isInRole(Identities identity, Permissions permission); + + static void tssCleanup(Context*); + + private: + Context(Identities identity):identity(identity) {} + static boost::thread_specific_ptr& ptr(); + }; + + // Impersonates an identity for the lifetime of the object + class Impersonator + { + Context current; + Context* previous; + public: + Impersonator(Identities identity); + ~Impersonator(); + }; + } +} + diff --git a/App/solver/Constraint.h b/App/solver/Constraint.h new file mode 100644 index 0000000..822261d --- /dev/null +++ b/App/solver/Constraint.h @@ -0,0 +1,682 @@ +#pragma once + +#include "solver/SolverConfig.h" +#include "solver/ConstraintJacobian.h" +#include "solver/SolverBody.h" +#include "v8kernel/SimBody.h" +#include "v8world/RotateJoint.h" + +#include "simd/simd.h" + +namespace RBX +{ + +class PGSSolver; +class DebugSerializer; + +// +// ConstraintVariables: inputs to the solver, initialized by the constraint interface +// +class ConstraintVariables +{ +public: + void serialize( DebugSerializer& s ) const; + + static RBX_SIMD_INLINE void setReaction( ConstraintVariables* _vars, const Vector3& _r ) + { + _vars[0].reaction = _r.x; + _vars[1].reaction = _r.y; + _vars[2].reaction = _r.z; + } + + static RBX_SIMD_INLINE void setReaction( ConstraintVariables* _vars, float x, float y ) + { + _vars[0].reaction = x; + _vars[1].reaction = y; + } + + static RBX_SIMD_INLINE void setImpulse( ConstraintVariables* _vars, const Vector3& _i ) + { + _vars[0].impulse = _i.x; + _vars[1].impulse = _i.y; + _vars[2].impulse = _i.z; + } + + static RBX_SIMD_INLINE void setImpulse( ConstraintVariables* _vars, float x, float y ) + { + _vars[0].impulse = x; + _vars[1].impulse = y; + } + + static RBX_SIMD_INLINE void setMinImpulses( ConstraintVariables* _vars, const Vector3& _min ) + { + _vars[0].minImpulseValue = _min.x; + _vars[1].minImpulseValue = _min.y; + _vars[2].minImpulseValue = _min.z; + } + + static RBX_SIMD_INLINE void setMinImpulses( ConstraintVariables* _vars, float x, float y ) + { + _vars[0].minImpulseValue = x; + _vars[1].minImpulseValue = y; + } + + static RBX_SIMD_INLINE void setMaxImpulses( ConstraintVariables* _vars, const Vector3& _max ) + { + _vars[0].maxImpulseValue = _max.x; + _vars[1].maxImpulseValue = _max.y; + _vars[2].maxImpulseValue = _max.z; + } + + static RBX_SIMD_INLINE void setMaxImpulses( ConstraintVariables* _vars, float x, float y ) + { + _vars[0].maxImpulseValue = x; + _vars[1].maxImpulseValue = y; + } + + static RBX_SIMD_INLINE void gatherComponents( simd::v4f& _impulses, simd::v4f& _reactions, simd::v4f& _min, simd::v4f& _max, const ConstraintVariables& _vars0 ) + { + _min = simd::splat< 0 >( simd::v4f( _vars0.v ) ); + _max = simd::splat< 1 >( simd::v4f( _vars0.v ) ); + _reactions = simd::splat< 2 >( simd::v4f( _vars0.v ) ); + _impulses = simd::splat< 3 >( simd::v4f( _vars0.v ) ); + } + + static RBX_SIMD_INLINE void gatherComponents( simd::v4f& _impulses, simd::v4f& _reactions, simd::v4f& _min, simd::v4f& _max, const ConstraintVariables& _vars0, const ConstraintVariables& _vars1 ) + { + transpose2x4( _min, _max, _reactions, _impulses, simd::v4f(_vars0.v), simd::v4f(_vars1.v) ); + } + + static RBX_SIMD_INLINE void gatherComponents( simd::v4f& _impulses, simd::v4f& _reactions, simd::v4f& _min, simd::v4f& _max, const ConstraintVariables& _vars0, const ConstraintVariables& _vars1, const ConstraintVariables& _vars2 ) + { + transpose3x4( _min, _max, _reactions, _impulses, (simd::v4f)_vars0.v, (simd::v4f)_vars1.v, (simd::v4f)_vars2.v ); + } + + static RBX_SIMD_INLINE void gatherComponents( simd::v4f& _impulses, simd::v4f& _reactions, simd::v4f& _min, simd::v4f& _max, const ConstraintVariables& _vars0, const ConstraintVariables& _vars1, const ConstraintVariables& _vars2, const ConstraintVariables& _vars3 ) + { + transpose( _min, _max, _reactions, _impulses, (simd::v4f)_vars0.v, (simd::v4f)_vars1.v, (simd::v4f)_vars2.v, (simd::v4f)_vars3.v ); + } + + // Values that must be set by the Constraint::buildEquation + // Inputs expected by the solver + union + { + struct + { + float minImpulseValue; + float maxImpulseValue; + + // Constraint must set this to the desired reaction + float reaction; + + // The Constraint must set this to the impulse computed in the previous frame or 0.0f if it is not available. + // This will contain the result. + float impulse; + }; + simd::v4f_pod v; + }; +}; + +// +// MovingRegression: Fit best 2nd degree curve to the last few data points +// +class MovingRegression +{ +public: + MovingRegression() + { + lastPoint = 0.0f; + lastTangent = 0.0f; + lastCurvature = 0.0f; + confidence = 0.0f; + } + + inline float testFitNextDataPointZeroOrder( float y ) const + { + float predicted = lastPoint; + return confidence * std::abs( y - predicted ) / ( std::max( std::abs( y ), std::abs( predicted ) ) + 0.00001f ) ; + } + + inline float testFitNextDataPointFirstOrder( float y ) const + { + float predicted = lastPoint + lastTangent; + return confidence * std::abs( y - predicted ) / ( std::max( std::abs( y ), std::abs( predicted ) ) + 0.00001f ) ; + } + + inline float testFitNextDataPointSecondOrder( float y ) const + { + float predicted = lastPoint + lastTangent + lastCurvature; + return confidence * std::abs( y - predicted ) / ( std::max( std::abs( y ), std::abs( predicted ) ) + 0.00001f ) ; + } + + float predict( ) const + { + return lastPoint; + } + + void addDataPoint( float y, float weight ) + { + float newTangent = y - lastPoint; + float newCurvature = newTangent - lastTangent; + lastCurvature = newCurvature; + lastTangent = newTangent; + lastPoint = y; + confidence += 0.1f * ( 1.0f - confidence ); + } + + void serialize( DebugSerializer& s) const; + + float confidence; + float lastPoint; + float lastTangent; + float lastCurvature; +}; + +// +// Cached values for each constraint equation +// +class ConstraintCache +{ +public: + ConstraintCache(): + velocityImpulse( 0.0f ), + velocityReaction( 0.0f ), + positionImpulse( 0.0f ), + positionReaction( 0.0f ), + // These need to be initialized to the values in SolverConfig! + velocitySor( 1.9f ), + positionSor( 1.9f ), + velocityCacheDamping( 1.0f ), + positionCacheDamping( 1.0f ) { } + + void cache( const ConstraintVariables& _velocityStage, const ConstraintVariables& _positionStage, float _sorVel, float _sorPos, bool _isCollision, const SolverConfig& config ); + + inline void readCache( ConstraintVariables& _velocityStage, ConstraintVariables& _positionStage, float& _sorVel, float& _sorPos ) const + { + _velocityStage.impulse = velocityImpulse; + _sorVel = velocitySor; + _velocityStage.reaction = velocityReaction; + _positionStage.impulse = positionImpulse; + _sorPos = positionSor; + _positionStage.reaction = positionReaction; + } + + void serialize( DebugSerializer& s ) const; + + float velocityImpulse; + float velocityReaction; + float velocitySor; + float velocityCacheDamping; + float positionImpulse; + float positionReaction; + float positionSor; + float positionCacheDamping; + + MovingRegression velocityImpulseRegression; + MovingRegression positionImpulseRegression; +}; + +// +// Constraint definition: Base class for all constraints and collision classes +// +class Constraint +{ +public: + enum Types + { + Types_Collision, // Special constraint type: only generated inside the solver from ContactConnectors + Types_Align2Axes, + Types_BallInSocket, + Types_AngularVelocity, + Types_LinearVelocity, + Types_AchievePosition, + Types_BodyAngularVelocity, + Types_LinearSpring, + Types_LegacyBreakableBallInSocket, + Types_LegacyAngularVelocity, + Types_Count + }; + + // Number of degrees of freedom constrained + inline unsigned getDimension() const { return dimensions; } + + inline bool isBroken() const { return broken; } + + // Read from the constraint cache, and call the overloaded build equation + // This should only be called by the solver + inline void restoreCacheAndBuildEquation( + ConstraintJacobianPair* _jacobian, + ConstraintVariables* _velocityStage, + ConstraintVariables* _positionStage, + float* _sorVel, + float* _sorPos, + boost::uint8_t* _useBlock, + const SolverBodyDynamicProperties& _bodyA, + const SolverBodyDynamicProperties& _bodyB, + const SolverConfig& _solverConfig, + float _dt ); + + // Write into the constraint cache + // This should only be called by the solver + inline void cache( + const ConstraintVariables* _velocityStage, + const ConstraintVariables* _positionStage, + const float* _sorVel, const float* _sorPos, + const SolverConfig& _config ); + + // Each breakable constraint will need to implement this, and return /true/ if the constraint changes state to broken + // This should only be called by the solver + inline void updateBrokenState( + const ConstraintVariables* _velocityStage, + const ConstraintVariables* _positionStage, + const SolverConfig& _config ); + + void setBodyA( Body* _a ) { bodyA = _a; } + void setBodyB( Body* _b ) { bodyB = _b; } + const Body* getBodyA() const { return bodyA; } + const Body* getBodyB() const { return bodyB; } + Body* getBodyA() { return bodyA; } + Body* getBodyB() { return bodyB; } + Types getType() const { return type; } + + virtual ~Constraint(); + + void setUID( boost::uint64_t _index ) { uid = _index; } + bool hasValidUID() const { return uid != 0; } + boost::uint64_t getUID() const { return uid; } + + // After the PGS has updated all i ts iterations, the last iteration reaction delta is passed in as parameter + enum Convergence + { + Convergence_Converges, + Convergence_Diverges, + Convergence_Undetermined + }; + virtual Convergence testPGSConvergence( const float* _disp, const float* _residuals, const float* _deltaResiduals, const SolverConfig& _solverConfig ) { return Convergence_Converges; } + + virtual void serialize( DebugSerializer& s ) const; + +protected: + const ConstraintCache& getCache( unsigned d ) const { return cacheData[ d ]; } + ConstraintCache& getCache( unsigned d ) { return cacheData[ d ]; } + + inline Constraint( Types _type, Body* _bodyA, Body* _bodyB, uint8_t _dimensions ); + +private: + // Main constraint interface to the solver + // Initializes the Jacobian and ConstraintVariables for the two stages + virtual void buildEquation( ConstraintJacobianPair* _jacobian, boost::uint8_t* _useBlock, ConstraintVariables* _velocityStage, ConstraintVariables* _positionStage, const SolverBodyDynamicProperties& _bodyA, const SolverBodyDynamicProperties& _bodyB, const SolverConfig& _config, float _dt ) = 0; + + // Each breakable constraint will need to implement this, and return /true/ if the constraint changes state to broken + virtual bool computeBrokenState( + const ConstraintVariables* _velocityStage, + const ConstraintVariables* _positionStage, + const SolverConfig& _config ) const { return false; } + + // Disable copy constructs + Constraint( const Constraint& ); + Constraint& operator=( const Constraint& ); + +protected: + uint8_t dimensions; + Types type : 8; + bool broken : 1; +private: + Body* bodyA; + Body* bodyB; + ConstraintCache* cacheData; + boost::uint64_t uid; // Current index if registered in the solver +}; + +// +// Constraint: inline implementation +// +inline Constraint::Constraint( Types _type, Body* _bodyA, Body* _bodyB, uint8_t _dimensions ): type( _type ), dimensions( _dimensions ), bodyA( _bodyA ), bodyB( _bodyB ), uid( 0 ), broken( false ) +{ + RBXASSERT( _bodyA != NULL ); + cacheData = new ConstraintCache[ dimensions ]; +} + +#ifdef __RBX_NOT_RELEASE +static inline void checkConstraintVariables( const ConstraintVariables& _vars ) +{ + RBXASSERT( !RBX::Math::isNanInf( _vars.impulse ) ); + RBXASSERT( !RBX::Math::isNanInf( _vars.reaction ) ); + RBXASSERT( !RBX::Math::isNan( _vars.minImpulseValue ) ); + RBXASSERT( !RBX::Math::isNan( _vars.maxImpulseValue ) ); +} + +static inline void checkJacobian( const ConstraintJacobianPair& _j ) +{ + RBXASSERT( !RBX::Math::isNanInfVector3( _j.a.lin ) ); + RBXASSERT( !RBX::Math::isNanInfVector3( _j.b.lin ) ); + RBXASSERT( !RBX::Math::isNanInfVector3( _j.a.ang ) ); + RBXASSERT( !RBX::Math::isNanInfVector3( _j.b.ang ) ); +} +#endif + +RBX_SIMD_INLINE void Constraint::restoreCacheAndBuildEquation( + ConstraintJacobianPair* __restrict _jacobian, + ConstraintVariables* __restrict _varsVel, + ConstraintVariables* __restrict _varsPos, + float* __restrict _sorVel, + float* __restrict _sorPos, + boost::uint8_t* _useBlock, + const SolverBodyDynamicProperties& _bodyA, + const SolverBodyDynamicProperties& _bodyB, + const SolverConfig& _solverConfig, + float _dt ) +{ + for( unsigned i = 0; i < dimensions; i++ ) + { + // Initialize to some reasonable default values + _varsVel[i].minImpulseValue = -std::numeric_limits::infinity(); + _varsVel[i].maxImpulseValue = std::numeric_limits::infinity(); + + _varsPos[i].minImpulseValue = -std::numeric_limits::infinity(); + _varsPos[i].maxImpulseValue = std::numeric_limits::infinity(); + + _jacobian[i].reset(); + + // Unless specified by the constraint, use the entire constraint as a Block in the Gauss-Seidel + _useBlock[i] = _solverConfig.blockPGSEnabled; + + // Read previous frame impulse/SOR/reaction values + getCache(i).readCache(_varsVel[i],_varsPos[i], _sorVel[i], _sorPos[i]); + + // Optionally disable the cache + _varsVel[i].impulse = _solverConfig.velCacheDamping * _varsVel[i].impulse; + _varsPos[i].impulse = _solverConfig.posCacheDamping * _varsPos[i].impulse; + } + + buildEquation( _jacobian, _useBlock, _varsVel, _varsPos, _bodyA, _bodyB, _solverConfig, _dt ); + + // Run some sanity checks +#ifdef __RBX_NOT_RELEASE + for( unsigned i = 0; i < dimensions; i++ ) + { + checkConstraintVariables( _varsVel[ i ] ); + checkConstraintVariables( _varsPos[ i ] ); + checkJacobian( _jacobian[ i ] ); + } +#endif +} + +RBX_SIMD_INLINE void Constraint::updateBrokenState( + const ConstraintVariables* _velocityStage, + const ConstraintVariables* _positionStage, + const SolverConfig& _config ) +{ + if( !broken ) + { + broken = computeBrokenState(_velocityStage, _positionStage, _config); + } +} + +inline void Constraint::cache( const ConstraintVariables* _velocityStage, const ConstraintVariables* _positionStage, const float* _sorVel, const float* _sorPos, const SolverConfig& _config ) +{ + // Cache Constraint base class data + for( unsigned i = 0; i < dimensions; i++ ) + { + cacheData[ i ].cache( _velocityStage[ i ], _positionStage[ i ], _sorVel[ i ], _sorPos[ i ], getType() == Types_Collision, _config ); + } +} + +// +// ConstraintBallInSocket +// +class ConstraintBallInSocket: public Constraint +{ +public: + ConstraintBallInSocket( Body* _bodyA, Body* _bodyB ): Constraint( Constraint::Types_BallInSocket, _bodyA, _bodyB, 3 ) { } + void buildEquation( ConstraintJacobianPair* _jacobian, boost::uint8_t* _useBlock, ConstraintVariables* _velocityStage, ConstraintVariables* _positionStage, const SolverBodyDynamicProperties& _bodyA, const SolverBodyDynamicProperties& _bodyB, const SolverConfig& _config, float _dt ) override; + + inline void setPivotA( const Vector3& _pivotA ) { pointA = _pivotA; } + inline void setPivotB( const Vector3& _pivotB ) { pointB = _pivotB; } + + Convergence testPGSConvergence( const float* _disp, const float* _residuals, const float* _deltaResiduals, const SolverConfig& _solverConfig ) override; + void serialize( DebugSerializer& s ) const override; + +private: + // Points on object A and B in object space, relative to center of mass + Vector3 pointA; + Vector3 pointB; +}; + +// +// ConstraintLegacyBreakableBallInSocket +// +class ConstraintLegacyBreakableBallInSocket: public Constraint +{ +public: + ConstraintLegacyBreakableBallInSocket( Body* _bodyA, Body* _bodyB ): Constraint( Constraint::Types_LegacyBreakableBallInSocket, _bodyA, _bodyB, 3 ), pointA(0.0f), pointB(0.0f), broken( false ), maxNormalForce( std::numeric_limits::infinity() ) + { + setNormalOnA( Vector3(1.0f, 0.0f, 0.0f ) ); + } + void buildEquation( ConstraintJacobianPair* _jacobian, boost::uint8_t* _useBlock, ConstraintVariables* _velocityStage, ConstraintVariables* _positionStage, const SolverBodyDynamicProperties& _bodyA, const SolverBodyDynamicProperties& _bodyB, const SolverConfig& _config, float _dt ) override; + + void setPivotA( const Vector3& _pivotA ) { pointA = _pivotA; } + void setPivotB( const Vector3& _pivotB ) { pointB = _pivotB; } + void setNormalOnA( const Vector3& _normal ); + void setMaxNormalForce( float _maxForce ) { maxNormalForce = _maxForce; } + bool computeBrokenState( + const ConstraintVariables* _velocityStage, + const ConstraintVariables* _positionStage, + const SolverConfig& _config ) const override; + + void serialize( DebugSerializer& s ) const override; + +private: + // Points on object A and B in object space, relative to center of mass + Vector3 pointA; + Vector3 pointB; + Vector3 normalA; + Vector3 tangentA1; + Vector3 tangentA2; + float maxNormalForce; + bool broken; +}; + +// +// ConstraintAlign2Axes +// +class ConstraintAlign2Axes: public Constraint +{ +public: + ConstraintAlign2Axes( Body* _bodyA, Body* _bodyB ); + void buildEquation( ConstraintJacobianPair* _jacobian, boost::uint8_t* _useBlock, ConstraintVariables* _velocityStage, ConstraintVariables* _positionStage, const SolverBodyDynamicProperties& _bodyA, const SolverBodyDynamicProperties& _bodyB, const SolverConfig& _config, float _dt ) override; + + void setAxisA( const Vector3& a ); + void setAxisB( const Vector3& b ); + Vector3 getAxisA() const { return axisA; } + Vector3 getAxisB() const { return axisB; } + + Convergence testPGSConvergence( const float* _disp, const float* _residuals, const float* _deltaResiduals, const SolverConfig& _solverConfig ) override; + void serialize( DebugSerializer& s ) const override; + +private: + // Axis on body A in object space + Vector3 axisA; + + // 2 normal axes to the axis on body B + // In object space + Vector3 axisB; + Vector3 orthogonalAxisB1; + Vector3 orthogonalAxisB2; + + // Cache + Vector3 worldSpaceOrthogonalB1; + Vector3 worldSpaceOrthogonalB2; +}; + +// +// ConstraintAngularVelocity +// +class ConstraintAngularVelocity: public Constraint +{ +public: + ConstraintAngularVelocity( Body* _bodyA, Body* _bodyB ): Constraint( Constraint::Types_AngularVelocity, _bodyA, _bodyB, 1 ), + maxForce( 0.0f ), + desiredAngularVelocity( 0.0f ) { } + void buildEquation( ConstraintJacobianPair* _jacobian, boost::uint8_t* _useBlock, ConstraintVariables* _velocityStage, ConstraintVariables* _positionStage, const SolverBodyDynamicProperties& _bodyA, const SolverBodyDynamicProperties& _bodyB, const SolverConfig& _config, float _dt ) override; + + void setAxisA( const Vector3& a ) { axisA = a; } + void setAxisB( const Vector3& b ) { axisB = b; } + void setDesiredAngularVelocity( float _v ) { desiredAngularVelocity = _v; } + void setMaxForce( float _f ) { maxForce = _f; } + + void serialize( DebugSerializer& s ) const override; + +private: + Vector3 axisA; + Vector3 axisB; + float maxForce; + float desiredAngularVelocity; +}; + +// +// ConstraintLinearVelocity +// +class ConstraintLinearVelocity: public Constraint +{ +public: + ConstraintLinearVelocity( Body* _bodyA, Body* _bodyB ): Constraint( Constraint::Types_LinearVelocity, _bodyA, _bodyB, 3 ), maxForce(0.0f), desiredVelocity(0.0f, 0.0f, 0.0f) { } + + void buildEquation( ConstraintJacobianPair* _jacobian, boost::uint8_t* _useBlock, ConstraintVariables* _velocityStage, ConstraintVariables* _positionStage, const SolverBodyDynamicProperties& _bodyA, const SolverBodyDynamicProperties& _bodyB, const SolverConfig& _config, float _dt ) override; + + void setDesiredVelocity( const Vector3& _v ) { desiredVelocity = _v; } + void setMaxForce( const Vector3& _f ) { maxForce = _f; } + + void serialize( DebugSerializer& s ) const override; + +private: + Vector3 maxForce; + Vector3 desiredVelocity; +}; + +class ConstraintLinearSpring: public Constraint +{ +public: + ConstraintLinearSpring( Body* _bodyA, Body* _bodyB ): Constraint( Constraint::Types_LinearSpring, _bodyA, _bodyB, 3 ), pivotA(0.0f), pivotB(0.0f), maxForce(0.0f), p(0.0f), d(0.0f) { } + + void buildEquation( ConstraintJacobianPair* _jacobian, boost::uint8_t* _useBlock, ConstraintVariables* _velocityStage, ConstraintVariables* _positionStage, const SolverBodyDynamicProperties& _bodyA, const SolverBodyDynamicProperties& _bodyB, const SolverConfig& _config, float _dt ) override; + + void setPivotA( const Vector3& _pivotA ) { pivotA = _pivotA; } + void setPivotB( const Vector3& _pivotB ) { pivotB = _pivotB; } + void setMaxForce( const Vector3& _f ) { maxForce = _f; } + void setPD( float _p, float _d ) { p = _p; d = _d; } + + void serialize( DebugSerializer& s ) const override; + +private: + Vector3 pivotA; + Vector3 pivotB; + Vector3 maxForce; + float p, d; +}; + +class ConstraintAchievePosition: public Constraint +{ +public: + ConstraintAchievePosition( Body* _bodyA, Body* _bodyB ): Constraint( Constraint::Types_AchievePosition, _bodyA, _bodyB, 3 ), maxForce( 0.0f ), targetVelocity( 0.0f ) { } + + void buildEquation( ConstraintJacobianPair* _jacobian, boost::uint8_t* _useBlock, ConstraintVariables* _velocityStage, ConstraintVariables* _positionStage, const SolverBodyDynamicProperties& _bodyA, const SolverBodyDynamicProperties& _bodyB, const SolverConfig& _config, float _dt ) override; + + void setPivotA( const Vector3& _pivotA ) { pivotA = _pivotA; } + void setPivotB( const Vector3& _pivotB ) { pivotB = _pivotB; } + void setTargetVelocity( const Vector3& _v ) { targetVelocity = _v; } + void setMaxForce( const Vector3& _f ) { maxForce = _f; } + void setMinForce( const Vector3& _f ) { minForce = _f; } + + void serialize( DebugSerializer& s ) const override; + +private: + Vector3 pivotA; + Vector3 pivotB; + Vector3 maxForce; + Vector3 minForce; + Vector3 targetVelocity; +}; + +class ConstraintBodyAngularVelocity: public Constraint +{ +public: + ConstraintBodyAngularVelocity( Body* _bodyA, Body* _bodyB ): Constraint( Constraint::Types_BodyAngularVelocity, _bodyA, _bodyB, 3 ), targetAngularVelocity( 0.0f ), maxTorque( 0.0f ), minTorque( 0.0f ), useIntegratedVelocities( false ) { } + + void buildEquation( ConstraintJacobianPair* _jacobian, boost::uint8_t* _useBlock, ConstraintVariables* _velocityStage, ConstraintVariables* _positionStage, const SolverBodyDynamicProperties& _bodyA, const SolverBodyDynamicProperties& _bodyB, const SolverConfig& _config, float _dt ) override; + + void serialize( DebugSerializer& s ) const override; + + // The vector values need to be provided in object space of body A + void setTarget( const Vector3& v ) { targetAngularVelocity = v; } + void setMaxTorque( const Vector3& v ) { maxTorque = v; } + void setMinTorque( const Vector3& v ) { minTorque = v; } + void setUseIntegratedVelocities( bool flag ) { useIntegratedVelocities = flag; } + +private: + Vector3 targetAngularVelocity; + Vector3 maxTorque; + Vector3 minTorque; + bool useIntegratedVelocities; +}; + +class ConstraintLegacyAngularVelocity: public Constraint +{ +public: + ConstraintLegacyAngularVelocity( Body* _bodyA, Body* _bodyB ): Constraint( Constraint::Types_LegacyAngularVelocity, _bodyA, _bodyB, 3 ), targetAngularVelocity( 0.0f ), maxTorque( 0.0f ), minTorque( 0.0f ), useIntegratedVelocities( false ) { } + + void buildEquation( ConstraintJacobianPair* _jacobian, boost::uint8_t* _useBlock, ConstraintVariables* _velocityStage, ConstraintVariables* _positionStage, const SolverBodyDynamicProperties& _bodyA, const SolverBodyDynamicProperties& _bodyB, const SolverConfig& _config, float _dt ) override; + + void serialize( DebugSerializer& s ) const override; + + // The vector values need to be provided in object space of body A + void setTarget( const Vector3& v ) { targetAngularVelocity = v; } + void setMaxTorque( const Vector3& v ) { maxTorque = v; } + void setMinTorque( const Vector3& v ) { minTorque = v; } + void setUseIntegratedVelocities( bool flag ) { useIntegratedVelocities = flag; } + +private: + Vector3 targetAngularVelocity; + Vector3 maxTorque; + Vector3 minTorque; + bool useIntegratedVelocities; +}; + +// +// ConstraintCollision +// +class ConstraintCollision: public Constraint +{ +public: + ConstraintCollision( Body* _bodyA, Body* _bodyB ): Constraint( Constraint::Types_Collision, _bodyA, _bodyB, 3 ) + { + cachedTangent1 = Vector3( 1.0f, 0.0f, 0.0f ); + getCache(0).positionSor = 1.0f; + getCache(0).velocitySor = 1.0f; + getCache(1).positionSor = 1.0f; + getCache(1).velocitySor = 1.0f; + getCache(2).positionSor = 1.0f; + getCache(2).velocitySor = 1.0f; + } + + void buildEquation( ConstraintJacobianPair* _jacobian, boost::uint8_t* _useBlock, ConstraintVariables* _velocityStage, ConstraintVariables* _positionStage, const SolverBodyDynamicProperties& _bodyA, const SolverBodyDynamicProperties& _bodyB, const SolverConfig& _config, float _dt ) override; + void setNormal( const Vector3& _normal ) { normal = _normal; } + void setPointA( const Vector3& _pointA ) { pointA = _pointA; } + void setDepth( float _d ) { depth = _d; } + void setFriction( float _f ) { friction = _f; } + void setResititution( float _r ) { restitution = _r; } + + Convergence testPGSConvergence( const float* _disp, const float* _residuals, const float* _deltaResiduals, const SolverConfig& _solverConfig ) override; + void serialize( DebugSerializer& s ) const override; +private: + Vector3 normal; + Vector3 pointA; + float depth; + float friction; + float restitution; + + // Cache + Vector3 cachedTangent1; +}; + +} diff --git a/App/solver/ConstraintJacobian.h b/App/solver/ConstraintJacobian.h new file mode 100644 index 0000000..ec614d9 --- /dev/null +++ b/App/solver/ConstraintJacobian.h @@ -0,0 +1,274 @@ +#pragma once + +#include "solver/SolverConfig.h" +#include "solver/SolverContainers.h" +#include "G3D/Vector3.h" + +#include "boost/limits.hpp" +#include "boost/math/constants/constants.hpp" +#include "boost/container/vector.hpp" + +#include + +#include "simd/simd.h" +#include "rbx/ArrayDynamic.h" + +namespace RBX +{ + +class DebugSerializer; +class ConstraintJacobianPair; + +// +// Use class definition rather than typedef so we can forward declare +// +class BodyPairIndices: public std::pair< int, int > +{ +public: + inline BodyPairIndices() { } + inline BodyPairIndices( int a, int b ): std::pair< int, int >( a, b ) { } +}; + +// +// VirtualDisplacement: http://en.wikipedia.org/wiki/Virtual_displacement +// A 6-dimensional vector with a linear part and angular part +// + +// This is a version that allows conversion from non-simd to simd types. +// The non-simd is still used by integration. +class VirtualDisplacementPOD +{ +public: + void serialize( DebugSerializer& s ) const; + union + { + simd::v4f_pod linV4; + Vector3Pod lin; + }; + union + { + simd::v4f_pod angV4; + Vector3Pod ang; + }; +}; + +// This is a version that can be worked with in simd +// It's only ever used on the stack +class VirtualDisplacement +{ +public: + RBX_SIMD_INLINE VirtualDisplacement( ){ } + RBX_SIMD_INLINE VirtualDisplacement( const simd::v4f& linear, const simd::v4f& angular ): lin( linear ), ang( angular ) { } + RBX_SIMD_INLINE VirtualDisplacement( const VirtualDisplacementPOD& _v ): lin( _v.linV4 ), ang( _v.angV4 ) { } + RBX_SIMD_INLINE operator VirtualDisplacementPOD() + { + VirtualDisplacementPOD r; + r.linV4 = lin; + r.angV4 = ang; + return r; + } + + RBX_SIMD_INLINE void reset() + { + lin = simd::zerof(); + ang = simd::zerof(); + } + + RBX_SIMD_INLINE simd::v4f getLin() const { return lin; } + RBX_SIMD_INLINE simd::v4f getAng() const { return ang; } + + void serialize( DebugSerializer& s ) const; + +private: + simd::v4f lin; + simd::v4f ang; +}; + +// +// Array of virtual displacements +// +class VirtualDisplacementArray +{ +public: + RBX_SIMD_INLINE VirtualDisplacementArray( size_t _size, size_t alignment ): size( _size ), data( _size, ArrayNoInit(), alignment ) + { } + + inline void reset() + { + VirtualDisplacement z( simd::zerof(), simd::zerof() ); + for( size_t i = 0; i < size; i++ ) + { + data[ i ] = z; + } + } + + const VirtualDisplacementPOD* getData() const { return data.data(); } + VirtualDisplacementPOD* getData() { return data.data(); } + RBX_SIMD_INLINE size_t getSize() const { return size; } + + RBX_SIMD_INLINE VirtualDisplacementPOD operator[]( int i ) const + { + RBXASSERT_VERY_FAST( (size_t)i < size ); + return data[ i ]; + } + + RBX_SIMD_INLINE VirtualDisplacementPOD& operator[]( int i ) + { + RBXASSERT_VERY_FAST( (size_t)i < size ); + return data[ i ]; + } + + void serialize( DebugSerializer& s ) const; + +private: + size_t size; + ArrayDynamic< VirtualDisplacementPOD > data; +}; + +// +// Effective mass vectors: inverse mass matrix * jacobian +// +class EffectiveMass +{ +public: + RBX_SIMD_INLINE EffectiveMass() { } + RBX_SIMD_INLINE EffectiveMass( const simd::v4f& linear, const simd::v4f& angular ): lin( linear ), ang( angular ) { } + RBX_SIMD_INLINE void applyMultiplier( const simd::v4f& m ) + { + lin = m * lin; + ang = m * ang; + } + RBX_SIMD_INLINE void reset() + { + lin = simd::zerof(); + ang = simd::zerof(); + } + RBX_SIMD_INLINE simd::v4f getLin() const { return lin; } + RBX_SIMD_INLINE simd::v4f getAng() const { return ang; } + +private: + simd::v4f lin; + simd::v4f ang; +}; + +class EffectiveMassPair +{ +public: + RBX_SIMD_INLINE EffectiveMassPair( const EffectiveMass& _a, const EffectiveMass& _b ): a( _a ), b( _b ) { } + + RBX_SIMD_INLINE EffectiveMassPair( ) { } + + RBX_SIMD_INLINE void reset() + { + a.reset(); + b.reset(); + } + + RBX_SIMD_INLINE void applyMultipliers( const simd::v4f& mA, const simd::v4f& mB ) + { + a.applyMultiplier( mA ); + b.applyMultiplier( mB ); + } + + RBX_SIMD_INLINE simd::v4f getLinA() const { return a.getLin(); } + RBX_SIMD_INLINE simd::v4f getLinB() const { return b.getLin(); } + RBX_SIMD_INLINE simd::v4f getAngA() const { return a.getAng(); } + RBX_SIMD_INLINE simd::v4f getAngB() const { return b.getAng(); } + + RBX_SIMD_INLINE EffectiveMass getPartA() const { return a; } + RBX_SIMD_INLINE EffectiveMass getPartB() const { return b; } + + void serialize( DebugSerializer& s ) const; + +private: + EffectiveMass a; + EffectiveMass b; +}; + +// +// Jacobian of a binary constraint +// +class ConstraintJacobian +{ +public: + RBX_SIMD_INLINE void reset() + { + linV4 = simd::zerof(); + angV4 = simd::zerof(); + } + + union + { + Vector3Pod lin; + simd::v4f_pod linV4; + }; + union + { + Vector3Pod ang; + simd::v4f_pod angV4; + }; +}; + +class ConstraintJacobianPair +{ +public: + class LinA; + class LinB; + class AngA; + class AngB; + + template< class PartSelect > + RBX_SIMD_INLINE simd::v4f get() const; + + RBX_SIMD_INLINE simd::v4f getLinA() const { return a.linV4; } + RBX_SIMD_INLINE simd::v4f getLinB() const { return b.linV4; } + RBX_SIMD_INLINE simd::v4f getAngA() const { return a.angV4; } + RBX_SIMD_INLINE simd::v4f getAngB() const { return b.angV4; } + + template< class PartSelect > + RBX_SIMD_INLINE void set( const simd::v4f& v ); + + RBX_SIMD_INLINE void setLinA( const simd::v4f& v ) { a.linV4 = v; } + RBX_SIMD_INLINE void setLinB( const simd::v4f& v ) { b.linV4 = v; } + RBX_SIMD_INLINE void setAngA( const simd::v4f& v ) { a.angV4 = v; } + RBX_SIMD_INLINE void setAngB( const simd::v4f& v ) { b.angV4 = v; } + + RBX_SIMD_INLINE void reset() + { + a.reset(); + b.reset(); + } + + RBX_SIMD_INLINE simd::v4f dot( const EffectiveMassPair& _v ) const + { + simd::v4f partA = getLinA() * _v.getLinA() + getAngA() * _v.getAngA(); + simd::v4f partB = getLinB() * _v.getLinB() + getAngB() * _v.getAngB(); + simd::v4f r = partA + partB; + return simd::splat<0>(r) + simd::splat<1>(r) + simd::splat<2>(r); + } + + void serialize( DebugSerializer& s ) const; + + ConstraintJacobian a; + ConstraintJacobian b; +}; + +template< > +RBX_SIMD_INLINE simd::v4f ConstraintJacobianPair::get< ConstraintJacobianPair::LinA >() const { return a.linV4; } +template< > +RBX_SIMD_INLINE simd::v4f ConstraintJacobianPair::get< ConstraintJacobianPair::LinB >() const { return b.linV4; } +template< > +RBX_SIMD_INLINE simd::v4f ConstraintJacobianPair::get< ConstraintJacobianPair::AngA >() const { return a.angV4; } +template< > +RBX_SIMD_INLINE simd::v4f ConstraintJacobianPair::get< ConstraintJacobianPair::AngB >() const { return b.angV4; } + +template< > +RBX_SIMD_INLINE void ConstraintJacobianPair::set< ConstraintJacobianPair::LinA >( const simd::v4f& v ) { a.linV4 = v; } +template< > +RBX_SIMD_INLINE void ConstraintJacobianPair::set< ConstraintJacobianPair::LinB >( const simd::v4f& v ) { b.linV4 = v; } +template< > +RBX_SIMD_INLINE void ConstraintJacobianPair::set< ConstraintJacobianPair::AngA >( const simd::v4f& v ) { a.angV4 = v; } +template< > +RBX_SIMD_INLINE void ConstraintJacobianPair::set< ConstraintJacobianPair::AngB >( const simd::v4f& v ) { b.angV4 = v; } + +} diff --git a/App/solver/DebugSerializer.h b/App/solver/DebugSerializer.h new file mode 100644 index 0000000..e71674e --- /dev/null +++ b/App/solver/DebugSerializer.h @@ -0,0 +1,193 @@ +#pragma once + +#include "util/G3DCore.h" +#include "util/Quaternion.h" + +#include "boost/type_traits.hpp" +#include "boost/utility.hpp" +#include "boost/cstdint.hpp" +#include "boost/container/map.hpp" + +#include "simd/simd.h" +#include "rbx/ArrayDynamic.h" + +#include +#include +#include + +namespace RBX +{ + +class DebugSerializer; + +// Compile time 'Has X Method' implementation using 'Substitution Failure is not an Error' +template< typename T > +struct HasSerializeMethod +{ +private: + template struct SFINAE {}; + template static char Test(SFINAE*); + template static int Test(...); +public: + static const bool value = sizeof(Test(0)) == sizeof(char); +}; + +class DebugSerializerScope +{ +public: + DebugSerializerScope( DebugSerializer& s ); + ~DebugSerializerScope(); + +private: + DebugSerializer& serializer; + size_t reservedBufferIndex; + size_t currentSize; +}; + +class DebugSerializer +{ +public: + void clear() + { + data.clear(); + } + + template< class T > + typename boost::enable_if< boost::is_arithmetic< T >, DebugSerializer >::type& storeAt( const T& t, size_t index ) + { + union + { + T t; + char bytes[ sizeof( T ) ]; + } u; + u.t = t; + for( size_t i = 0; i < sizeof( T ); i++ ) + { + data[ index + i ] = u.bytes[ i ]; + } + return *this; + } + + template< class T > + typename boost::enable_if< boost::is_arithmetic< T >, DebugSerializer >::type& operator&( const T& t ) + { + size_t index = data.size(); + data.resize( data.size() + sizeof( T ) ); + storeAt( t, index ); + return *this; + } + + template< class T > + typename boost::enable_if< boost::is_enum< T >, DebugSerializer >::type& operator&( const T& t ) + { + union + { + T t; + boost::uint8_t bytes[ sizeof( T ) ]; + } u; + u.t = t; + for( size_t i = 0; i < sizeof( T ); i++ ) + { + data.push_back( u.bytes[ i ] ); + } + return *this; + } + + DebugSerializer& operator&( const Vector3& v ) + { + *this & v.x & v.y & v.z; + return *this; + } + + DebugSerializer& operator&( const Quaternion& q ) + { + *this & q.x & q.y & q.z & q.w; + return *this; + } + + DebugSerializer& operator&( const Matrix3& m ) + { + *this & m.row( 0 ) & m.row( 1 ) & m.row( 2 ); + return *this; + } + + DebugSerializer& operator&( const simd::v4f& v ) + { + *this & simd::extractSlow( v, 0 ) & simd::extractSlow( v, 1 ) & simd::extractSlow( v, 2 ) & simd::extractSlow( v, 3 ); + return *this; + } + + template< class T, class U > + DebugSerializer& operator&( const std::pair< T, U >& p ) + { + *this & p.first & p.second; + return *this; + } + + template< class T > + DebugSerializer& operator&( const ArrayBase& v ) + { + *this & boost::uint32_t( v.size() ); + for( const auto& e : v ) + { + *this & e; + } + return *this; + } + + template< class T > + DebugSerializer& operator&( const std::vector& v ) + { + *this & boost::uint32_t( v.size() ); + for( const auto& e : v ) + { + *this & e; + } + return *this; + } + + template< class T > + typename boost::enable_if_c< HasSerializeMethod< T >::value && !boost::is_pointer< T >::value, DebugSerializer >::type& operator&( const T& t ) + { + t.serialize( *this ); + return *this; + } + + template< class T > + typename boost::enable_if_c< HasSerializeMethod< T >::value, DebugSerializer >::type& operator&( const T* const t ) + { + t->serialize( *this ); + return *this; + } + + DebugSerializer& tag( const char* name ) + { + boost::uint8_t length = strlen(name); + *this & length; + for( boost::uint8_t i = 0; i < length; i++ ) + { + *this & name[ i ]; + } + return *this; + } + + std::vector< char > data; +}; + +inline DebugSerializerScope::DebugSerializerScope( DebugSerializer& s ): serializer( s ) +{ + reservedBufferIndex = s.data.size(); + size_t size = 0; + s & size; +// size_t checkSum = 0; +// s & checkSum; + currentSize = s.data.size(); +} + +inline DebugSerializerScope::~DebugSerializerScope() +{ + size_t size = serializer.data.size() - currentSize; + serializer.storeAt( size, reservedBufferIndex ); +} + +} diff --git a/App/solver/Solver.h b/App/solver/Solver.h new file mode 100644 index 0000000..163a9b0 --- /dev/null +++ b/App/solver/Solver.h @@ -0,0 +1,193 @@ +#pragma once + +#include "solver/SolverConfig.h" +#include "solver/Constraint.h" +#include "solver/SolverContainers.h" +#include "solver/SolverProfiler.h" +#include "solver/SolverSerializer.h" + +#include "v8kernel/SimBody.h" + +#include "boost/unordered/unordered_map.hpp" +#include "boost/unordered/unordered_set.hpp" +#include "boost/container/vector.hpp" +#include + +namespace RBX +{ + +class ContactConnector; +class RotateJoint; +class ContactManifold; +class Body; + +typedef std::pair< boost::uint64_t, boost::uint64_t > BodyUIDPair; + +class InconsistentBodyPair +{ +public: + bool operator<( const InconsistentBodyPair& pair ) const + { + return bodyPair < pair.bodyPair; + } + + Body* bodyA; + Body* bodyB; + BodyUIDPair bodyPair; + Constraint::Convergence convergence; +}; + +class OrderedConnector +{ +public: + ContactConnector* connector; + bool swap; +}; + +class BadConnector +{ +public: + Vector3 position; + Constraint::Convergence convergence; +}; + +class PGSSolver +{ +public: + PGSSolver(); + + // Add/remove SimBodies + // High priority bodies will be simulated even if throttled + void addSimBody( SimBody* body, bool highPriority ); + void removeSimBody( SimBody* body ); + + // Add/remove constraints + void addConstraint( Constraint* _constraint ); + void removeConstraint( Constraint* _constraint ); + + // Solver + // If throttled is set to true, only high priority bodies will be simulated. + void solve( const std::vector< ContactConnector* >& connectors, float dt, boost::uint64_t debugTime, bool throttled ); + void solvePositions( const std::vector< ContactConnector* >& _contactConnectors ); + // Exactly like it was before the physics analyzer (sleeping islands) were submitted + void solveLegacy( const std::vector< ContactConnector* >& _contactConnectors, float _dt, boost::uint64_t debugTime, bool _throttled ); + + // Create and delete contact manifolds + // The parameters are uid's of both Object instances. + void addContactManifold( boost::uint64_t _uidA, boost::uint64_t _uidB ); + void removeContactManifold( boost::uint64_t _uidA, boost::uint64_t _uidB ); + + // Clear body cache + void clearBodyCache( boost::uint64_t _uid ); + + // Switch inconsistent constraint detector + void setInconsistentConstraintDetectorEnabled( bool value ) { inconsistentConstraintDetectorEnabled = value; } + void setPhysicsAnalyzerBreakOnIssue( bool value ) { physicsAnalyzerBreakOnIssue = value; } + bool getPhysicsAnalyzerBreakOnIssue( ) const { return physicsAnalyzerBreakOnIssue; } + + const ArrayBase< InconsistentBodyPair >& getInconsistentBodyPairs() const { return inconsistentBodyPairs; } + const ArrayBase< ArrayDynamic< boost::uint64_t > >& getInconsistentBodies() const { return inconsistentBodies; } + + void dumpLog( bool enable ); + void setUserId( int id ) { userId = id; } + +private: + void solveInternal( const std::vector< ContactConnector* >& connectors, float dt, boost::uint64_t debugTime, bool throttled, const SolverConfig& _solverConfig ); + void solveIsland( const ArrayDynamic< Constraint* >& constraints, const ArrayDynamic< SimBody* >& selectedSimBodies, + float _dt, const SolverConfig& _solverConfig ); + ContactManifold* updateContactManifold( const BodyUIDPair& _pairId, const ArrayBase< OrderedConnector >& _manifold ); + size_t addContactConnectors( ArrayDynamic< ContactManifold* >& _activeManifolds, const std::vector< ContactConnector* >& _connectors, const boost::unordered_set< SimBody* >& _simBodies ); + void initAnchoredObjects( + ArrayDynamic< SolverBodyDynamicProperties >& _bodyVariableData, + ArrayDynamic< SolverBodyStaticProperties >& _bodyStaticData, + ArrayDynamic< SolverBodyMassAndInertia >& _massAndInertia, + ArrayDynamic< float >& _effectiveMassMultipliers, + VirtualDisplacementArray& _velocityDeltas, + VirtualDisplacementArray& _positionDeltas, + int offsetToAnchoredObjects, + const ArrayBase< SimBody* >& _anchoredBodyList, + const SolverConfig& _config ) const; + void integratePositionsAndUpdateSimBodies( + SimBody* const * _simBodies, + SolverBodyDynamicProperties* const _bodyVariableData, + const SolverBodyStaticProperties* const _bodyStaticData, + size_t _simBodyCount, + const VirtualDisplacementArray& _velocityDeltas, + const VirtualDisplacementArray& _positionDeltas, + float _dt ); + void integratePositionsIgnoreVelocitiesAndUpdateSimBodies( + SimBody* const * _simBodies, + SolverBodyDynamicProperties* const _bodyVariableData, + const SolverBodyStaticProperties* const _bodyStaticData, + size_t _simBodyCount, + const VirtualDisplacementArray& _positionDeltas, + float _dt ); + void detectInconsistentConstraints( + VirtualDisplacementArray& positionDeltasSIMD, + ArrayBase< ConstraintVariables >& positionStage, + const ArrayBase< ConstraintJacobianPair >& jacobians, + const ArrayBase< ConstraintJacobianPair >& preconditionedJacobiansPosStage, + const ArrayBase< EffectiveMassPair >& effectiveMassesPos, + const ArrayBase< float >& sorPos, + const ArrayBase< boost::uint8_t >& dimensions, + const ArrayBase< boost::uint32_t >& offsets, + const ArrayBase< BodyPairIndices >& simBodyPairs, + const ArrayBase< Constraint* >& constraints, + size_t collisionCount, + const SolverConfig& solverConfig ); + + + boost::uint64_t constraintUIDGenerator; + // Pure constraints - not including the Collision constraints + SolverOrderedMap< boost::uint64_t, Constraint* >::Type pureConstraintSet; + + SolverUnorderedMap< BodyUIDPair, ContactManifold* >::Type contactManifolds; + + class SolverBodyCache + { + public: + void serialize( DebugSerializer& s ) const; + Vector3 virDPosStageLin; + Vector3 virDPosStageAng; + Vector3 virDVelStageLin; + Vector3 virDVelStageAng; + Vector3 linearVelocity; + Vector3 angularVelocity; + Vector3 integratedLinearVelocity; + Vector3 integratedAngularVelocity; + SimBody* simBodyDebug; + }; + + SolverUnorderedMap< boost::uint64_t, SolverBodyCache >::Type bodyCache; + boost::unordered_set< SimBody* > simBodies; + boost::unordered_set< SimBody* > highPrioritySimBodies; + + // Inconsistent constraint detector + bool inconsistentConstraintDetectorEnabled; + bool physicsAnalyzerBreakOnIssue; + ArrayDynamic< InconsistentBodyPair > inconsistentBodyPairs; + ArrayDynamic< ArrayDynamic< boost::uint64_t > > inconsistentBodies; + + // Serializer + SolverSerializer serializer; + + // Profilers + SolverProfiler gatherCollisionsProfiler; + SolverProfiler islandSplitProfiler; + SolverProfiler integrateVelocitiesProfiler; + SolverProfiler initAnchoredBodiesProfiler; + SolverProfiler buildEquationsProfiler; + SolverProfiler computeEffectiveMassesProfiler; + SolverProfiler preconditioningProfiler; + SolverProfiler multiplyEffectiveMassMultipliersProfiler; + SolverProfiler initVirDProfiler; + SolverProfiler kernelProfiler; + SolverProfiler integratePositionsProfiler; + SolverProfiler writeCacheProfiler; + SolverProfiler solverProfiler; + + bool dumpLogSwitch; + int userId; +}; + +} diff --git a/App/solver/SolverBody.h b/App/solver/SolverBody.h new file mode 100644 index 0000000..500f286 --- /dev/null +++ b/App/solver/SolverBody.h @@ -0,0 +1,288 @@ +#pragma once + +#include "G3D/Vector3.h" +#include "simd/simd.h" +#include "solver/SolverContainers.h" +#include "solver/ConstraintJacobian.h" + +namespace RBX +{ + class DebugSerializer; + + // + // Internal representation of dynamic properties of a body (for both simulated and anchored) + // + class SolverBodyDynamicProperties + { + public: + void serialize( DebugSerializer& s ) const; + + Vector3 integratedLinearVelocity; + Vector3 integratedAngularVelocity; + Matrix3 orientation; + Vector3 position; + Vector3 linearVelocity; + Vector3 angularVelocity; + }; + + // + // Symmetric matrix for use as inertia matrix + // + class SymmetricMatrix + { + public: + RBX_SIMD_INLINE Vector3 operator*( const Vector3& v ) const + { + Vector3 r; + r.x = diagonals.x * v.x + offDiagonals.x * v.y + offDiagonals.y * v.z; + r.y = offDiagonals.x * v.x + diagonals.y * v.y + offDiagonals.z * v.z; + r.z = offDiagonals.y * v.x + offDiagonals.z * v.y + diagonals.z * v.z; + return r; + } + + RBX_SIMD_INLINE SymmetricMatrix operator*( float s ) const + { + SymmetricMatrix r; + r.diagonals = s * diagonals; + r.offDiagonals = s * offDiagonals; + return r; + } + + void serialize( DebugSerializer& s ) const; + + // [ d0 a b ] + // [ a d1 c ] + // [ b c d2] + Vector3Pod diagonals; // [d0, d1, d2] + Vector3Pod offDiagonals; // [a, b, c] + }; + + class SymmetricMatrixPOD + { + public: + simd::v4f_pod diagonals; + simd::v4f_pod offDiagonals; + }; + + // + // SymmetricMatrixSIMD + // + class SymmetricMatrixSIMD + { + public: + RBX_SIMD_INLINE SymmetricMatrixSIMD( const float* _m ) + { + diagonals = simd::load3( _m ); + offDiagonals = simd::load3( _m + 3 ); + } + + RBX_SIMD_INLINE SymmetricMatrixSIMD( const simd::v4f& _diagonal, const simd::v4f& _offDiagonal ): diagonals( _diagonal ), offDiagonals( _offDiagonal ) { } + + RBX_SIMD_INLINE SymmetricMatrixSIMD( const SymmetricMatrixPOD& _m ): diagonals( _m.diagonals ), offDiagonals( _m.offDiagonals ) { } + + template< int row, int column > + RBX_SIMD_INLINE simd::v4f get() const; + + RBX_SIMD_INLINE simd::v4f operator*( const simd::v4f& v ) const + { + simd::v4f t0 = diagonals * v; + simd::v4f t1 = simd::permute<0, 2, 1, 3>( offDiagonals ) * simd::permute< 1, 2, 0, 3>( v ); + simd::v4f t2 = simd::permute<1, 0, 2, 3>( offDiagonals ) * simd::permute< 2, 0, 1, 3>( v ); + return t0 + t1 + t2; + } + + RBX_SIMD_INLINE void invert() + { + simd::v4f x00x00x02x01 = simd::shuffle< 0, 0, 1, 0 >( diagonals, offDiagonals ); + simd::v4f x11x00x01x12 = simd::shuffle< 1, 0, 0, 2 >( diagonals, offDiagonals ); + simd::v4f x22x00x12x02 = simd::shuffle< 2, 0, 2, 1 >( diagonals, offDiagonals ); + + simd::v4f x00x00x11x22 = simd::permute< 0, 0, 1, 2 >( diagonals ); + simd::v4f x12x12x02x01 = simd::permute< 2, 2, 1, 0 >( offDiagonals ); + + simd::v4f r = x00x00x02x01 * x11x00x01x12 * x22x00x12x02 - x00x00x11x22 * x12x12x02x01 * x12x12x02x01; + simd::v4f d = simd::splat< 0 >( r ) + simd::splat< 2 >( r ) + simd::splat< 3 >( r ); + simd::v4f dInv = ( simd::splat( 1.0f ) / d ); + + simd::v4f x11x22x00 = simd::permute< 1, 2, 0, 3 >( diagonals ); + simd::v4f x22x00x11 = simd::permute< 2, 0, 1, 3 >( diagonals ); + simd::v4f x12x02x01 = simd::permute< 2, 1, 0, 3 >( offDiagonals ); + simd::v4f newDiagonals = dInv * ( x11x22x00 * x22x00x11 - x12x02x01 * x12x02x01 ); + + simd::v4f x12x01x02 = simd::permute< 2, 0, 1, 3 >( offDiagonals ); + simd::v4f x02x12x01 = simd::permute< 1, 2, 0, 3 >( offDiagonals ); + simd::v4f x01x02x12 = offDiagonals; + simd::v4f x22x11x00 = simd::permute< 2, 1, 0, 3 >( diagonals ); + simd::v4f newOffdiagonals = dInv * ( x12x01x02 * x02x12x01 - x01x02x12 * x22x11x00 ); + + diagonals = newDiagonals; + offDiagonals = newOffdiagonals; + } + + simd::v4f diagonals; + simd::v4f offDiagonals; + }; + + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrixSIMD::get<0,0>() const { return simd::splat<0>( diagonals ); } + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrixSIMD::get<1,1>() const { return simd::splat<1>( diagonals ); } + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrixSIMD::get<2,2>() const { return simd::splat<2>( diagonals ); } + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrixSIMD::get<0,1>() const { return simd::splat<0>( offDiagonals ); } + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrixSIMD::get<0,2>() const { return simd::splat<1>( offDiagonals ); } + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrixSIMD::get<1,2>() const { return simd::splat<2>( offDiagonals ); } + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrixSIMD::get<1,0>() const { return get<0,1>(); } + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrixSIMD::get<2,0>() const { return get<0,2>(); } + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrixSIMD::get<2,1>() const { return get<1,2>(); } + + static RBX_SIMD_INLINE SymmetricMatrixSIMD operator*( const simd::v4f& s, const SymmetricMatrixSIMD& m ) + { + return SymmetricMatrixSIMD( s * m.diagonals, s* m.offDiagonals ); + } + + // + // SymmetricMatrix2SIMD + // + class SymmetricMatrix2SIMD + { + public: + RBX_SIMD_INLINE SymmetricMatrix2SIMD( ) { } + + RBX_SIMD_INLINE SymmetricMatrix2SIMD( const simd::v4f& d00, const simd::v4f& d11, const simd::v4f& d01 ) + { + m = simd::gatherX( d00, d01, d01, d11 ); + } + + RBX_SIMD_INLINE void load( const float* _m ) + { + m = simd::form( _m[0], _m[2], _m[2], _m[1] ); + } + + RBX_SIMD_INLINE void form( const simd::v4f& d00, const simd::v4f& d11, const simd::v4f& d01 ) + { + m = simd::gatherX( d00, d01, d01, d11 ); + } + + RBX_SIMD_INLINE simd::v4f operator*( const simd::v4f& v ) const + { + simd::v4f t0 = m * simd::permute< 0, 0, 1, 1 >( v ); + simd::v4f t1 = simd::permute< 2, 3, 0, 1 >( t0 ); + return t0 + t1; + } + + RBX_SIMD_INLINE void invert() + { + simd::v4f xt = simd::splat< 1 >( m ); + simd::v4f det = simd::splat< 0 >( m ) * simd::splat< 3 >( m ) - xt * xt; + simd::v4f t = simd::select< 0, 1, 1, 0 >( simd::splat( 1.0f ), simd::splat( -1.0f ) ) * simd::permute< 3, 2, 1, 0 >( m ); + m = ( t / det ); + } + + template< int row, int column > + RBX_SIMD_INLINE simd::v4f get() const; + + simd::v4f m; + }; + + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrix2SIMD::get<0,0>() const { return simd::splat<0>( m ); } + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrix2SIMD::get<1,0>() const { return simd::splat<1>( m ); } + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrix2SIMD::get<0,1>() const { return simd::splat<1>( m ); } + template< > + RBX_SIMD_INLINE simd::v4f SymmetricMatrix2SIMD::get<1,1>() const { return simd::splat<3>( m ); } + + class SolverBodyMassAndInertia + { + public: + void serialize( DebugSerializer& s ) const; + + RBX_SIMD_INLINE SymmetricMatrixSIMD getInvInertiaVelStage() const + { + return inertiaSIMD; + } + + RBX_SIMD_INLINE SymmetricMatrixSIMD getInvInertiaPosStage( float scale ) const + { + simd::v4f inertiaScale = simd::splat( scale ) * simd::splat( posToVelMassRatio ); + SymmetricMatrixSIMD r( inertiaSIMD ); + return inertiaScale * r; + } + + RBX_SIMD_INLINE simd::v4f getInvMassVelStage() const + { + return simd::splat( massInvVelStage ); + } + + RBX_SIMD_INLINE simd::v4f getInvMassPosStage() const + { + return simd::splat( massInvVelStage * posToVelMassRatio ); + } + + class VelStage; + class PosStage; + + template< class StageSelect > + RBX_SIMD_INLINE simd::v4f getInvMass() const; + + template< class StageSelect > + RBX_SIMD_INLINE SymmetricMatrixSIMD getInvInertia( float scale ) const; + + union + { + struct + { + Vector3Pod inertiaDiagonal; + float massInvVelStage; + Vector3Pod inertiaOffDiagonal; + float posToVelMassRatio; + }; + SymmetricMatrixPOD inertiaSIMD; + }; + }; + + template<> + RBX_SIMD_INLINE simd::v4f SolverBodyMassAndInertia::getInvMass< SolverBodyMassAndInertia::VelStage >() const + { + return getInvMassVelStage(); + } + + template<> + RBX_SIMD_INLINE simd::v4f SolverBodyMassAndInertia::getInvMass< SolverBodyMassAndInertia::PosStage >() const + { + return getInvMassPosStage(); + } + + template< > + RBX_SIMD_INLINE SymmetricMatrixSIMD SolverBodyMassAndInertia::getInvInertia< SolverBodyMassAndInertia::VelStage >( float scale ) const + { + return getInvInertiaVelStage(); + } + + template< > + RBX_SIMD_INLINE SymmetricMatrixSIMD SolverBodyMassAndInertia::getInvInertia< SolverBodyMassAndInertia::PosStage >( float scale ) const + { + return getInvInertiaPosStage( scale ); + } + + // + // Static properties of a body + // + class SolverBodyStaticProperties + { + public: + void serialize( DebugSerializer& s ) const; + + boost::uint64_t bodyUID; + boost::uint32_t guid; + bool isStatic; + }; +} diff --git a/App/solver/SolverConfig.h b/App/solver/SolverConfig.h new file mode 100644 index 0000000..5c9aa72 --- /dev/null +++ b/App/solver/SolverConfig.h @@ -0,0 +1,166 @@ +#pragma once + +#define ENABLE_SOR_CONSTRAINTS +#define ENABLE_SOR_COLLISIONS +#define ENABLE_LOCAL_SOR_MODULATION +#define ENABLE_HINGE_FRICTION +#define ENABLE_IMPULSE_CACHE_DAMPING_PER_EQUATION +//#define ENABLE_SOLVER_PROFILER +#define ENABLE_SOLVER_DEBUG_SERIALIZER +//#define MIN_NORM_REPROJECT +//#define PGS_MIN_NORM + +//#define DISABLE_ANGULAR_CONSTRAINTS + +namespace RBX +{ + +class SolverConfig +{ +public: + enum Type + { + Type_Default, + Type_InconsistencyDetector, + Type_PositionalCorrection, + }; + SolverConfig( Type type = Type_Default ); + + // + // Kernel + // + unsigned pgsIterations; + + // + // Collisions + // + + // Minimum normal velocity for restitution to be applied + float collisionRestitutionThreshold; + + // Ignore penetrations that are smaller than this parameter + float collisionPenetrationMargin; + + // Params for variable penetration margin + float collisionPenetrationMarginMax; + float collisionPenetrationMarginMin; + + // The max height of a bump that a rolling object (sphere) can have, in proportion to it's size, due to hitting an edge between two primitives. + float collisionPenetrationMarginMaxBumpProportions; + + // Damping of the penetration resolution + float collisionPenetrationResolutionDamping; + + // The velocity at which the penetration allowed will be minimum + float collisionPenetrationVelocityForMinMargin; + + // Threshold tangential velocity between static and dynamic friction + float collisionFrictionStaticToDynamicThreshold; + + // Tunning constant for static friction + float collisionFrictionStaticScale; + + // Tunning constant for dynamic friction + float collisionFrictionDynamicScale; + + // + // Align2Axes Constraint + // + + // Angular friction velocity stage + float align2AxesFrictionConstant; + + // Angular friction position stage + float align2AxesPositionStageFrictionConstant; + + // Maximum angle for angular correction in degrees + float align2AxesMaxCorrectiveAngle; + + float align2AxesCorrectionDamping; + + // + // BallInSocket + // + + // Maximum corrective distance + float ballInSocketMaxCorrectionByStabilization; + + float ballInSocketCorrectionDamping; + + // + // SOR Modulation + // + + // Create a common structure for these... + // Constraints + struct ModulationParams + { + float thresholdMax; + float thresholdMin; + float aggressiveValue; + float conservativeValue; + float easingUpToAggressive; + float easingDownToConservative; + }; + + ModulationParams sorConstraintsModulation; + ModulationParams sorCollisionsModulation; + ModulationParams cacheVStageModulation; + ModulationParams cachePStageModulation; + + // + // Stabilization + // + float stabilizationMassReductionPower; + float stabilizationInertiaScale; + + // + // Cache + // + float velCacheDamping; + float posCacheDamping; + bool constraintCachingEnabled; + + // + // Integration + // + float angularDamping; + bool updateSimBodies; + bool integrateOnlyPositions; + + // + // Block PGS + // + bool blockPGSEnabled; + + // + // SOR + // + float velocityStageSOREnabled; + float positionStageSOREnabled; + + // + // Virtual masses + // + bool virtualMassesEnabled; + + // + // Use sim islands + // + bool useSimIslands; + + // + // Conflicting constraints detector + // + bool inconsistentConstraintDetectorEnabled; + unsigned inconsistentConstraintMaxIterations; + + float inconsistentConstraintBallInSocketResidualThreshold; + float inconsistentConstraintDeltaThreshold; + float inconsistentConstraintAlign2AxesThreshold; + float inconsistentConstraintCollisionThreshold; + float inconsistentConstraintCollisionBaseThreshold; + bool printConvergenceDiagnostics; +}; + +} diff --git a/App/solver/SolverContainers.h b/App/solver/SolverContainers.h new file mode 100644 index 0000000..5d5811e --- /dev/null +++ b/App/solver/SolverContainers.h @@ -0,0 +1,91 @@ +#pragma once + +#include "solver/SolverConfig.h" +#include "boost/unordered/unordered_map.hpp" +#include "boost/container/vector.hpp" + +#include "rbx/DenseHash.h" +#include "util/G3DCore.h" + +#include + +namespace RBX +{ + +class SimBody; + +// If you want to have the map visible in the debugger, enable this +// #define SOLVER_DEBUG_MAP + +template< class K, class T > +struct SolverUnorderedMap +{ +#ifdef SOLVER_DEBUG_MAP + // Use a std map in non-release for easier debugger inspection + typedef std::map< K, T > Type; +#else + // This is faster than a std::map as it uses a hash table + typedef boost::unordered::unordered_map< K, T > Type; +#endif +}; + +template< class K, class T > +struct SolverOrderedMap +{ + typedef std::map< K, T > Type; +}; + +//typedef SolverUnorderedMap< const SimBody*, int >::Type BodyIndexation; +typedef DenseHashMap< const SimBody*, int > BodyIndexation; + +// It's so that we can use a Vector3 in unions +class Vector3Pod +{ +public: + void operator=( const Vector3& v ) + { + x = v.x; + y = v.y; + z = v.z; + } + + operator Vector3() const + { + return Vector3(x, y, z); + } + + float dot( const Vector3& v ) const + { + return x * v.x + y * v.y + z * v.z; + } + + Vector3Pod& operator+=( const Vector3& v ) + { + x += v.x; + y += v.y; + z += v.z; + return *this; + } + + float x, y, z; +}; + +inline Vector3Pod operator*( float s, const Vector3Pod& v ) +{ + Vector3Pod r; + r.x = s * v.x; + r.y = s * v.y; + r.z = s * v.z; + return r; +} + +inline Vector3Pod operator+( const Vector3Pod& u, const Vector3Pod& v ) +{ + Vector3Pod r; + r.x = u.x + v.x; + r.y = u.y + v.y; + r.z = u.z + v.z; + return r; +} + +} diff --git a/App/solver/SolverKernel.h b/App/solver/SolverKernel.h new file mode 100644 index 0000000..0944f42 --- /dev/null +++ b/App/solver/SolverKernel.h @@ -0,0 +1,94 @@ +#pragma once + +#include "solver/SolverConfig.h" +#include "boost/cstdint.hpp" +#include "rbx/ArrayDynamic.h" + +namespace RBX +{ + +class ConstraintJacobianPair; +class BodyPairIndices; +class SolverBodyStaticProperties; +class SolverBodyMassAndInertia; +class ConstraintVariables; +class VirtualDisplacementArray; +class VirtualDisplacementArray; +class EffectiveMassPair; + +void PGSComputeEffectiveMasses( + EffectiveMassPair* _effectiveMassesVelStage, + EffectiveMassPair* _effectiveMassesPosStage, + size_t _constraintCount, + const boost::uint8_t* _dimensions, + const ConstraintJacobianPair* _jacobians, + const BodyPairIndices* _pairs, + const SolverBodyMassAndInertia* _massAndIntertia, + const SolverConfig& _config ); + +void PGSApplyEffectiveMassMultipliers( + EffectiveMassPair* _effectiveMassesVelStage, + EffectiveMassPair* _effectiveMassesPosStage, + size_t _constraintCount, + const boost::uint8_t* _dimensions, + const float* _multipliers, + const BodyPairIndices* _pairs, + const SolverConfig& _config ); + +void PGSPreconditionConstraintEquations( + ConstraintJacobianPair* _preconditionedJacobiansVelStage, + ConstraintJacobianPair* _preconditionedJacobiansPosStage, + ConstraintVariables* _velocityStageVariables, + ConstraintVariables* _positionStageVariables, + size_t _constraintCount, + const boost::uint8_t* _dimensions, + const boost::uint8_t* _useBlock, + const float* __restrict _sorVel, + const float* __restrict _sorPos, + const ConstraintJacobianPair* _jacobians, + const EffectiveMassPair* _effectiveMassesVelStage, + const EffectiveMassPair* _effectiveMassesPosStage ); + +void PGSInitVirtualDisplacements( + VirtualDisplacementArray& _virDVel, + VirtualDisplacementArray& _virDPos, + const EffectiveMassPair* _effectiveMassesVelStage, + const EffectiveMassPair* _effectiveMassesPosStage, + size_t _constraintCount, + const boost::uint8_t* _dimensions, + const ConstraintVariables* __restrict _velStage, + const ConstraintVariables* __restrict _posStage, + const BodyPairIndices* _pairs, + const SolverConfig& _config ); + +void PGSSolveKernel( + ConstraintVariables* __restrict _velStage, + ConstraintVariables* __restrict _posStage, + VirtualDisplacementArray& _virDVel, + VirtualDisplacementArray& _virDPos, + size_t _constraintCount, + size_t _collisionCount, + const boost::uint8_t* _dimensions, + const BodyPairIndices* _pairs, + const ConstraintJacobianPair* _preconditionedJacobiansVelStage, + const ConstraintJacobianPair* _preconditionedJacobiansPosStage, + const EffectiveMassPair* _effectiveMassesVelStage, + const EffectiveMassPair* _effectiveMassesPosStage, + const SolverConfig& _config ); + +void PGSSolveKernelComputeErrors( + ArrayBase< float >& _residuals, + ArrayBase< float >& _deltaResiduals, + ArrayBase< ConstraintVariables >& _vars, + VirtualDisplacementArray& _virD, + size_t _constraintCount, + size_t _collisionCount, + size_t _bodyCount, + const boost::uint8_t* _dimensions, + const BodyPairIndices* _pairs, + const ConstraintJacobianPair* _jacobians, + const ConstraintJacobianPair* _preconditionedJacobians, + const EffectiveMassPair* _effectiveMasses, + const SolverConfig& _config ); + +} diff --git a/App/solver/SolverProfiler.h b/App/solver/SolverProfiler.h new file mode 100644 index 0000000..3670a7f --- /dev/null +++ b/App/solver/SolverProfiler.h @@ -0,0 +1,59 @@ +#pragma once + +#include "rbx/rbxTime.h" +#include "RbxAssert.h" +#include "util/standardout.h" + +namespace RBX +{ + +// +// Prints average timing after the given number of samples were taken +// +class SolverProfiler +{ +public: + SolverProfiler( int _samples, const char* _format ): maxSamples( _samples ), format( _format ) + { + accumulator = Time::Interval::zero(); + currentSamples = 0; + } + + void start() + { +#ifdef ENABLE_SOLVER_PROFILER + startTime = Time::now( Time::Precise ); +#endif + } + + void end() + { +#ifdef ENABLE_SOLVER_PROFILER + Time::Interval total = Time::now( Time::Precise ) - startTime; + accumulator += total; + currentSamples++; +#endif + } + + void printStats() + { +#ifdef ENABLE_SOLVER_PROFILER + static bool enable = true; + if( currentSamples >= maxSamples && enable ) + { + currentSamples = 0; + StandardOut::singleton()->printf( MESSAGE_OUTPUT, format, accumulator.msec() / maxSamples ); + accumulator = Time::Interval::zero(); + } +#endif + } + +private: + RBX::Time startTime; + Time::Interval accumulator; + const char* format; + int maxSamples; + int currentSamples; +}; + +} diff --git a/App/solver/SolverSerializer.h b/App/solver/SolverSerializer.h new file mode 100644 index 0000000..0f49bfa --- /dev/null +++ b/App/solver/SolverSerializer.h @@ -0,0 +1,189 @@ +#pragma once + +#include "solver/SolverConfig.h" +#include "solver/SolverContainers.h" +#include "solver/DebugSerializer.h" + +#include "util/standardout.h" + +#include "boost/filesystem.hpp" + +namespace RBX +{ + +class SolverSerializer +{ +public: + SolverSerializer(): enabled( false ), fileOpened( false ) { } + + void update( bool _enabled, int userId, boost::uint64_t debugTime ) + { +#ifdef ENABLE_SOLVER_DEBUG_SERIALIZER + bool switchState = ( enabled != _enabled ); + bool close = false; + if( switchState ) + { + if( !enabled ) + { + enabled = true; + } + else + { + enabled = false; + close = true; + } + } + else if( !enabled ) + { + return; + } + + static size_t bufferSize = 10 * 1024 * 1024; + + if( !fileOpened ) + { + debugSerializer.data.reserve( bufferSize + 1024 * 1024 ); + boost::filesystem::path path = boost::filesystem::temp_directory_path(); + path /= "ROBLOX"; + path /= "SolverLog_Client"; + path += boost::lexical_cast( userId ); + path += ".bin"; + myFile.open (path.c_str(), std::ios::out | std::ios::binary); + fileOpened = true; + debugSerializer & userId; + } + + if( close || debugSerializer.data.size() > bufferSize ) + { + myFile.write( debugSerializer.data.data(), debugSerializer.data.size() ); + debugSerializer.data.clear(); + } + + if( close && fileOpened ) + { + close = false; + enabled = false; + myFile.close(); + fileOpened = false; + } + + static boost::uint64_t frame = 0; + if( enabled ) + { + debugSerializer & frame; + debugSerializer & debugTime; + frame++; + } +#endif + } + + void serializeConstraints( const ArrayBase< Constraint* >& connectors ) + { + if( enabled ) + { + debugSerializer.tag("Connectors"); + debugSerializer & (boost::uint32_t)connectors.size(); + for( const auto* c : connectors ) + { + debugSerializer & (boost::uint8_t)c->getType() & c; + } + } + } + + void serializeForces( const ArrayBase< SimBody* >& simBodies, boost::uint32_t total ) + { + if( enabled ) + { + debugSerializer.tag("Forces"); + debugSerializer & total; + size_t i = 0; + for( i = 0; i < simBodies.size(); i++ ) + { + debugSerializer & simBodies[ i ]->getForce(); + debugSerializer & simBodies[ i ]->getTorque(); + debugSerializer & simBodies[ i ]->getImpulse(); + debugSerializer & simBodies[ i ]->getRotationallmpulse(); + } + Vector3 zero = Vector3::zero(); + for( ; i < total; i++ ) + { + debugSerializer & zero & zero & zero & zero; + } + } + } + + void serializeComputedImpulse( const ArrayBase< ConstraintVariables >& velocityStage, const ArrayBase< ConstraintVariables >& positionStage ) + { + if(enabled) + { + ArrayDynamic< float > impulses; + impulses.reserve(velocityStage.size()); + for( const auto& v : velocityStage ) + { + impulses.push_back(v.impulse); + } + debugSerializer & impulses; + impulses.clear(); + impulses.reserve(velocityStage.size()); + for( const auto& v : positionStage ) + { + impulses.push_back(v.impulse); + } + debugSerializer & impulses; + } + } + + template< class Cache > + void serializeBodyCache( const Cache& bodyCache ) + { + if( enabled ) + { + debugSerializer & boost::uint32_t( bodyCache.size() ); + for( const auto& it : bodyCache ) + { + debugSerializer & (boost::uint32_t)it.second.simBodyDebug->getBody()->getGuidIndex(); + debugSerializer & it.second; + } + } + } + + template< class T > + SolverSerializer& operator&( const T& t ) + { +#ifdef ENABLE_SOLVER_DEBUG_SERIALIZER + if( enabled ) + { + debugSerializer & t; + } +#endif + return *this; + } + + template< class T > + SolverSerializer& operator&( const ArrayDynamic< T >& t ) + { +#ifdef ENABLE_SOLVER_DEBUG_SERIALIZER + if( enabled ) + { + debugSerializer & static_cast< const ArrayBase< T >& >( t ); + } +#endif + return *this; + } + + SolverSerializer& tag( const char* name ) + { + if( enabled ) + { + debugSerializer.tag(name); + } + return *this; + } + + std::ofstream myFile; + bool fileOpened; + bool enabled; + DebugSerializer debugSerializer; +}; + +} diff --git a/App/stdafx.h b/App/stdafx.h new file mode 100644 index 0000000..121850a --- /dev/null +++ b/App/stdafx.h @@ -0,0 +1,22 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include +#include + +#include "rbx/boost.hpp" +#include "rbx/threadsafe.h" +#include "rbx/signal.h" +#include "rbx/TaskScheduler.Job.h" + +#include +#include + +#include "util/Name.h" +#include "util/Region3.h" +#include "reflection/YieldFunction.h" +#include "v8tree/Instance.h" +#include "V8DataModel/DataModel.h" + + diff --git a/App/tool/AdvDragTool.h b/App/tool/AdvDragTool.h new file mode 100644 index 0000000..fa08259 --- /dev/null +++ b/App/tool/AdvDragTool.h @@ -0,0 +1,50 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" + +/* + // End user tools + + GameTool (select top parts for dragging, close to character) + -> PartDragTool (called for dragging) + + GrabTool (drag parts and models in game) + -> DragTool (called for dragging) + + ArrowTool (i.e. - powerpoint - select, box select, shift select, drag) + -> DragTool (called for dragging) + + // Auxillary (private) + PartDragTool (drag a single part) + + GroupDragTool (drag a group of parts, or a model, or a selection of parts/models) + + DragTool + -> PartDragTool (part dragging) + -> GroupDragTool (>1 part, or model dragging) + + +*/ + +namespace RBX { + +// TEMP COMMENT: verify merging... + class Workspace; + class PartInstance; + class PVInstance; + + class AdvDragTool + { + public: + static shared_ptr onMouseDown(PartInstance* hitPart, + const Vector3& hitWorld, + const std::vector& dragInstances, + const shared_ptr& inputObject, + Workspace* workspace, + shared_ptr selectIfNoDrag); +// TEMP COMMENT: Verifying merge... + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/AdvLuaDragTool.h b/App/tool/AdvLuaDragTool.h new file mode 100644 index 0000000..881dc68 --- /dev/null +++ b/App/tool/AdvLuaDragTool.h @@ -0,0 +1,51 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Tool/ToolsArrow.h" +#include "V8DataModel/MouseCommand.h" +#include "Tool/AdvLuaDragger.h" +#include "Util/Math.h" +#include + +namespace RBX { + + class PartInstance; + + extern const char* const sAdvLuaDragTool; + + class AdvLuaDragTool : public Named + { + private: + typedef Named Super; + boost::shared_ptr advLuaDragger; + boost::weak_ptr selectIfNoDrag; + std::string cursor; + bool dragging; + G3D::Vector2 downPoint2d; + + bool canDrag(const shared_ptr& inputObject) const; + + ///////////////////////////////////////////////////////// + // MouseCommand + // + /*override*/ void onMouseIdle(const shared_ptr& inputObject); + /*override*/ void onMouseMove(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseUp(const shared_ptr& inputObject); + /*override*/ const std::string getCursorName() const { return cursor; } + /*override*/ shared_ptr onKeyDown(const shared_ptr& inputObject); + /*override*/ void setCursor(std::string newCursor) { cursor = newCursor; } + + public: + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + + AdvLuaDragTool( PartInstance* mousePart, + const Vector3& hitPointWorld, + const std::vector >& partArray, + Workspace* workspace, + shared_ptr selectIfNoDrag); + + ~AdvLuaDragTool(); + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/AdvLuaDragger.h b/App/tool/AdvLuaDragger.h new file mode 100644 index 0000000..6ccf9b9 --- /dev/null +++ b/App/tool/AdvLuaDragger.h @@ -0,0 +1,80 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Tree/Instance.h" + +namespace RBX { + + class Joint; + class PartInstance; + class ContactManager; + class AdvRunDragger; + + extern const char* const sAdvLuaDragger; + + class AdvLuaDragger + : public DescribedCreatable + { + private: + typedef enum {NO_PARTS, MOUSE_DOWN, DRAGGING, MOUSE_UP_DRAGGED, MOUSE_UP_NO_DRAG} DragPhase; + DragPhase dragPhase; + + std::vector > jointsIMade; + weak_ptr rootPart; + std::auto_ptr advRunDragger; // only if we have one part + + float hitPointHeight; + + typedef std::vector > WeakParts; + WeakParts dragParts; + weak_ptr mousePart; + Vector3 pointOnMousePart; + Vector3 hitPointOffset; //at the start of the drag process this the vector from + //the hitPoint on the background to hit point on the mouse part + + std::vector m_originalPositions; + + void tryStartDragging(const RBX::RbxRay& unitMouseRay); + void startDragging(); + void doDrag(const RBX::RbxRay& unitMouseRay); + bool getSnapHitPoint(PartInstance* part, const RBX::RbxRay& unitMouseRay, Vector3& hitPoint); + ContactManager& getContactManager(PartInstance* partInstance); + + const float breakFreeDistance(); // distance in studs at the mouse down point before movement + + void addPart(shared_ptr part); + /*override*/ bool askSetParent(const Instance* instance) const { return false; } + + public: + AdvLuaDragger(); + ~AdvLuaDragger(); + + void mouseDownPublic(shared_ptr _mousePart, + Vector3 _pointOnMousePart, + shared_ptr _dragParts); + + void mouseDown( shared_ptr _mousePart, + const Vector3& _pointOnMousePart, + const std::vector > _dragParts); + + void mouseMove(RBX::RbxRay mouseRay); // inefficient, but easier to have just one version + + void mouseUp(); + + const WeakParts& getParts() {return dragParts;} + + void rotateOnSnapFace(Vector3::Axis, const Matrix3& rotMatrix); + + void axisRotate(Vector3::Axis axis); + + bool isDragging() const {return dragPhase == DRAGGING;} + + bool didDrag() const {return dragPhase == MOUSE_UP_DRAGGED;} + + void toggleRunDraggerRotateMode( void ); + void toggleRunDraggerJointCreateMode( void ); + void alignPartToGrid( void ); + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/AdvMoveTool.h b/App/tool/AdvMoveTool.h new file mode 100644 index 0000000..41d58a0 --- /dev/null +++ b/App/tool/AdvMoveTool.h @@ -0,0 +1,109 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Tool/ToolsArrow.h" +#include "Tool/MegaDragger.h" +#include "AppDraw/HandleType.h" +#include "Util/NormalId.h" + +namespace RBX { + + class MegaDragger; + class Extents; + + class AdvMoveToolBase : public AdvArrowToolBase + { + protected: + bool dragging; + Ray dragRay; + int dragAxis; + int dragAxisDirection; + NormalId dragNormalId; + std::string cursor; + + //TODO: Have a single variable in Move and Rotate tool + mutable NormalId overHandleNormalId; + + private: + typedef AdvArrowToolBase Super; + std::auto_ptr megaDragger; + Vector2int16 downPoint2d; + + // dynamic - last point on the Ray we dragged to + // only works with one-stud grid... + Vector3 lastPoint3d; + + typedef std::map, float> PartsTransparencyCollection; + PartsTransparencyCollection origPartsTransparency; + + mutable Matrix3 mMultiRotation; + mutable Extents mExtents; + mutable bool mInitializedExtents; + + float snapRotationAngle(float Angle) const; + void saveAndModifyPartsTransparency(); + void restoreSavedPartsTransparency(); + + rbx::signals::scoped_connection selectionChangedConnection; + void setToSelection(); + + protected: + + virtual bool getLocalSpaceMode() const; + virtual bool getOverHandle(const shared_ptr& inputObject, Vector3& hitPointWorld, NormalId& normalId) const; + + bool getExtents(Extents& extents) const; + bool getExtentsAndLocation( + Extents& extents, + CoordinateFrame& location, + bool& isLocal ) const; + bool getOverHandle(const shared_ptr& inputObject) const; + + /*override*/ bool drawConnectors() const {return true;} // default mouse command no draw connectors + /*override*/ void onMouseIdle(const shared_ptr& inputObject); + /*override*/ void onMouseHover(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + /*override*/ void onMouseMove(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseUp(const shared_ptr& inputObject); + /*override*/ shared_ptr onKeyDown(const shared_ptr& inputObject); + + /*override*/ void render2d(Adorn* adorn); + /*override*/ void render3dAdorn(Adorn* adorn); + /*override*/ const std::string getCursorName() const {return cursor;} + /*override*/ void setCursor(std::string newCursor) {cursor = newCursor;} + + + /*implement*/ virtual Color3 getHandleColor() const = 0; + /*implement*/ virtual HandleType getDragType() const = 0; + + public: + AdvMoveToolBase(Workspace* workspace); + virtual ~AdvMoveToolBase() + {} + }; + + + extern const char* const sAdvMoveTool; + class AdvMoveTool : public Named + { + private: + /*override*/ Color3 getHandleColor() const {return Color3::orange();} + /*override*/ HandleType getDragType() const {return HANDLE_MOVE;} + /*override*/ void render2d(Adorn* adorn); + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + + void getGridXYUsingCamera(RBX::PartInstance *part, G3D::Vector3 &gridXDir, G3D::Vector3 &gridYDir); + + G3D::Vector3 originalLocation; + + public: + AdvMoveTool(Workspace* workspace) : Named(workspace) + {} + ~AdvMoveTool() {} + + /*override*/ shared_ptr isSticky() const {return Creatable::create(workspace);} + }; + + +} // namespace RBX diff --git a/App/tool/AdvRotateTool.h b/App/tool/AdvRotateTool.h new file mode 100644 index 0000000..20e0ec9 --- /dev/null +++ b/App/tool/AdvRotateTool.h @@ -0,0 +1,40 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Tool/AdvMoveTool.h" + +namespace RBX { + + extern const char* const sAdvRotateTool; + class AdvRotateTool : public Named + { + private: + typedef Named Super; + + int getNormalMask() const; + + mutable NormalId mOverHandleNormalId; + + protected: + + virtual Color3 getHandleColor() const {return Color3::green();} + virtual HandleType getDragType() const {return HANDLE_ROTATE;} + virtual bool getLocalSpaceMode() const; + virtual bool getOverHandle(const shared_ptr& inputObject, Vector3& hitPointWorld, NormalId& normalId) const; + + public: + AdvRotateTool(Workspace* workspace) : + Named(workspace), + mOverHandleNormalId(NORM_UNDEFINED) + {} + + /*override*/ shared_ptr isSticky() const {return Creatable::create(workspace);} + /*override*/ void render2d(Adorn* adorn); + /*override*/ void render3dAdorn(Adorn* adorn); + + }; + + + +} // namespace RBX diff --git a/App/tool/AdvRunDragger.h b/App/tool/AdvRunDragger.h new file mode 100644 index 0000000..6496c6d --- /dev/null +++ b/App/tool/AdvRunDragger.h @@ -0,0 +1,155 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/Contact.h" +#include "Util/Object.h" +#include "Util/G3DCore.h" +#include "Util/NormalId.h" +#include +#include "Tool/DragTypes.h" +#include "AppDraw/DrawAdorn.h" +#include "GfxBase/Adorn.h" +#include "GfxBase/IAdornable.h" + +//#define DEBUG_MULTIPLE_PARTS_DRAG + +namespace RBX { + class Primitive; + class PartInstance; + class Workspace; + typedef std::vector > WeakParts; + typedef std::vector Locations; + + class AdvRunDragger : public IAdornable + { + private: + class SnapInfo { + public: + Primitive* snap; + NormalId surface; + size_t mySurfaceId; + Vector3 hitWorld; + Vector3 lastDragSnap; + SnapInfo() + : snap(NULL) + , surface(NORM_UNDEFINED) + , mySurfaceId((size_t)-1) + , hitWorld(Vector3::inf()) + , lastDragSnap(Vector3::inf()) + {} + void updateHitFromSurface(const RbxRay& mouseRay); + void updateSurfaceFromHit(); + float hitOutsideExtents(); + }; + + // Initialized + // Poor man's version - ultimately, change all internally to shared_ptr's + weak_ptr dragPart; + std::vector dragParts; + WeakParts weakDragParts; + weak_ptr snapPart; + + bool isAdornable; + Workspace* workspace; + Primitive* drag; + Vector3 dragPointLocal; + Matrix3 dragOriginalRotation; + + // Carry forward data + SnapInfo snapInfo; + + // Set on every snap() call + RbxRay mouseRay; + + // Computed every snap() call + NormalId dragSurface; // computed + // Angled surface testing... + size_t myDragSurfaceId; // computed + Vector3 dragHitLocal; // computed + + Vector3 snapGridOrigin; + bool snapGridOriginNeedsUpdating; + + // modes + DRAG::DraggerGridMode gridMode; + bool jointCreateMode; + + // new multiple drag + boost::shared_ptr tempPart; + Locations originalLocations; + G3D::Array savedPrimsForMultiDrag; + PartInstance* primaryPart; + bool handleMultipleParts; + CoordinateFrame tempOriginalCF; + + SnapInfo createSnapSurface(Primitive* snap, G3D::Array* ignore = NULL); + bool moveDragPart(); + bool snapDragPart(bool supressGridSettings = false); + bool pushDragPart(const Vector3& snapNormal); + + void findSafeY(); + bool notTried(Primitive* check, const G3D::Array& tried); + bool adjacent(Primitive* p0, Primitive* p1); + SnapInfo rayHitsPart(const G3D::Array& triedSnap, bool forceAdjacent); + SnapInfo bestProximatePart(const G3D::Array& triedSnap, + Contact::ProximityTest proximityTest); + bool fallOffEdge(); + + bool fallOffPart(bool& snapped); + bool colliding(); + bool rayHitsCloserPart(); + bool tooCloseToCamera(); + + SnapInfo findSnap(const G3D::Array& triedSnap); + void findNoSnapPosition(const CoordinateFrame& original); + + void snapInfoFromSnapPart(); + void snapPartFromSnapInfo(); + + void savePrimsForMultiDrag(); + Contact* getFirstContact(Primitive*& prim); + Contact* getNextContact(Primitive*& prim, Contact* c); + + public: + AdvRunDragger(); + ~AdvRunDragger(); + void init( Workspace* _workspace, + weak_ptr _dragPart, + const Vector3& _dragPointWorld); + void initLocal( Workspace* _workspace, + weak_ptr _dragPart, + const Vector3& _dragPointLocal, + WeakParts _dragParts); + + bool snap(const RbxRay& mouseRay); + bool snapGroup(const RbxRay& _mouseRay); + bool getSnapHitPoint(PartInstance* part, const RbxRay& unitMouseRay, Vector3& hitPoint); + + void rotatePartAboutSnapFaceAxis( Vector3::Axis axis, const float& angleInRads ); + void rotatePart90DegAboutSnapFaceAxis( Vector3::Axis axis ); + + // Note: these will NOT update dragOriginalRotation + static void turnUpright(PartInstance* part); + static void rotatePart(PartInstance* part); + static void tiltPart(PartInstance* part, const CoordinateFrame& camera); + + void setGridMode( DRAG::DraggerGridMode mode ) { gridMode = mode; } + DRAG::DraggerGridMode getGridMode( void ) { return gridMode; } + void setJointCreateMode( bool mode ) { jointCreateMode = mode; } + bool getJointCreateMode( void ) { return jointCreateMode; } + CoordinateFrame getSnapSurfaceCoord( void ); + +#ifdef DEBUG_MULTIPLE_PARTS_DRAG + // IAdornable + bool shouldRender3dAdorn() const { return true; } + void render3dAdorn(Adorn* adorn); +#else + // IAdornable + bool shouldRender3dAdorn() const { return false; } +#endif + + static bool dragMultiPartsAsSinglePart; + }; +} // namespace + diff --git a/App/tool/AxisMoveTool.h b/App/tool/AxisMoveTool.h new file mode 100644 index 0000000..1c9631b --- /dev/null +++ b/App/tool/AxisMoveTool.h @@ -0,0 +1,55 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Tool/ToolsArrow.h" +#include "Tool/MegaDragger.h" +#include "AppDraw/HandleType.h" +#include "Util/NormalId.h" + +namespace RBX { + + class MegaDragger; + class Extents; + + class AxisToolBase : public ArrowToolBase + { + private: + typedef ArrowToolBase Super; + std::auto_ptr megaDragger; + std::string cursor; + bool dragging; + Vector2int16 downPoint2d; + RbxRay dragRay; + int dragAxis; + + // dynamic - last point on the Ray we dragged to + Vector3 lastPoint3d; + + bool getExtents(Extents& extents) const; + bool getOverHandle(const shared_ptr& inputObject) const; + bool getOverHandle(const shared_ptr& inputObject, Vector3& hitPointWorld, NormalId& normalId) const; + + /*override*/ bool drawConnectors() const {return true;} // default mouse command no draw connectors + + protected: + /*override*/ void onMouseIdle(const shared_ptr& inputObject); + /*override*/ void onMouseHover(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + /*override*/ void onMouseMove(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseUp(const shared_ptr& inputObject); + + /*override*/ void render2d(Adorn* adorn); + /*override*/ void render3dAdorn(Adorn* adorn); + /*override*/ const std::string getCursorName() const {return cursor;} + + /*implement*/ virtual Color3 getHandleColor() const = 0; + /*implement*/ virtual HandleType getDragType() const = 0; + + public: + AxisToolBase(Workspace* workspace); + }; + + + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/AxisRotateTool.h b/App/tool/AxisRotateTool.h new file mode 100644 index 0000000..5e1c654 --- /dev/null +++ b/App/tool/AxisRotateTool.h @@ -0,0 +1,25 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Tool/AxisMoveTool.h" + +namespace RBX { + + extern const char* const sAxisRotateTool; + class AxisRotateTool : public Named + { + private: + /*override*/ Color3 getHandleColor() const {return Color3::green();} + /*override*/ HandleType getDragType() const {return HANDLE_ROTATE;} + + public: + AxisRotateTool(Workspace* workspace) : Named(workspace) + {} + + /*override*/ shared_ptr isSticky() const {return Creatable::create(workspace);} + }; + + + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/CloneTool.h b/App/tool/CloneTool.h new file mode 100644 index 0000000..9422857 --- /dev/null +++ b/App/tool/CloneTool.h @@ -0,0 +1,32 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" + +namespace RBX { + + class PartInstance; + + extern const char* const sCloneTool; + class CloneTool : public Named + { + private: + shared_ptr clonePart; + + /*override*/ void onMouseIdle(const shared_ptr& inputObject); + /*override*/ const std::string getCursorName() const; + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + + public: + CloneTool(Workspace* workspace); + ~CloneTool(); + + /*override*/ shared_ptr isSticky() const {return Creatable::create(workspace);} + /*override*/ bool drawConnectors() const {return true;} // default mouse command no draw connectors + }; + + + + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/DragTool.h b/App/tool/DragTool.h new file mode 100644 index 0000000..b56d477 --- /dev/null +++ b/App/tool/DragTool.h @@ -0,0 +1,49 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" + +/* + // End user tools + + GameTool (select top parts for dragging, close to character) + -> PartDragTool (called for dragging) + + GrabTool (drag parts and models in game) + -> DragTool (called for dragging) + + ArrowTool (i.e. - powerpoint - select, box select, shift select, drag) + -> DragTool (called for dragging) + + // Auxillary (private) + PartDragTool (drag a single part) + + GroupDragTool (drag a group of parts, or a model, or a selection of parts/models) + + DragTool + -> PartDragTool (part dragging) + -> GroupDragTool (>1 part, or model dragging) + + +*/ + +namespace RBX { + + class Workspace; + class PartInstance; + class PVInstance; + + class DragTool + { + public: + static shared_ptr onMouseDown( PartInstance* hitPart, + const Vector3& hitWorld, + const std::vector& dragInstances, + const shared_ptr& inputObject, + Workspace* workspace, + shared_ptr selectIfNoDrag); + + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/DragTypes.h b/App/tool/DragTypes.h new file mode 100644 index 0000000..53ab5d9 --- /dev/null +++ b/App/tool/DragTypes.h @@ -0,0 +1,17 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +namespace RBX { + namespace DRAG { + + typedef enum {NO_UNJOIN_NO_JOIN, UNJOIN_JOIN, UNJOIN_NO_JOIN} JoinType; + + typedef enum {MOVE_DROP, MOVE_NO_DROP} MoveType; + + typedef enum {WEAK_MANUAL_JOINT, STRONG_MANUAL_JOINT, INFINITE_MANUAL_JOINT} ManualJointType; + + typedef enum {ONE_STUD, QUARTER_STUD, OFF} DraggerGridMode; + + } +} // namespace \ No newline at end of file diff --git a/App/tool/DragUtilities.h b/App/tool/DragUtilities.h new file mode 100644 index 0000000..668dfcd --- /dev/null +++ b/App/tool/DragUtilities.h @@ -0,0 +1,100 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/Object.h" +#include "Util/G3DCore.h" +#include "Util/Extents.h" +#include "Tool/Dragger.h" + +namespace RBX { + class Instance; + class PartInstance; + class Primitive; + class PVInstance; + class ContactManager; + class World; + + typedef std::vector > PartArray; + + class DragUtilities + { + private: + static bool hitObjectOrPlane(const ContactManager& contactManager, + const RbxRay& unitSearchRay, + const std::vector& ignorePrims, + Vector3& hit, + bool snapToGrid = true); + static bool hitObject( const ContactManager& contactManager, + const RbxRay& unitSearchRay, + const std::vector& ignorePrims, + Vector3& hit, + bool snapToGrid = true); + + public: + static bool notJoined(const PartArray& parts); + static bool notJoinedToOutsiders(const PartArray& parts); + + static void unJoinFromOutsiders(const PartArray& parts); + static void joinToOutsiders(const PartArray& parts); + + static void unJoin(const PartArray& parts); + static void join(const PartArray& parts); + static void joinWithInPartsOnly(const PartArray& parts); + + static void setDragging(const PartArray& parts); + static void stopDragging(const PartArray& parts); + + static void clean(const PartArray& parts); + static void move(const PartArray& parts, + CoordinateFrame from, + CoordinateFrame to); + static void move2(const PartArray& parts, + CoordinateFrame from, + CoordinateFrame to); + + static void alignToGrid(PartInstance* part); // force alignement to grid + static void clean(PartInstance* part); // clean up alignment if already aligned + static void moveByDelta(PartInstance* part, const Vector3& delta, bool snapToWorld); + + static void pvsToParts(const std::vector& pvInstances, PartArray& parts); + static void instancesToParts(const std::vector& instances, PartArray& parts); + static void instancesToParts(const Instances& instances, PartArray& parts); + static void removeDuplicateParts(PartArray& parts); + + static World* partsToPrimitives(const PartArray& parts, G3D::Array& primitives); + static void partsToPrimitives(const PartArray& parts, std::vector& primitives); + static void partsToPrimitives(const PartArray& parts, std::vector& primitives); + + static Extents computeExtents(const PartArray& parts); + + static Vector3 hitObjectOrPlane(const PartArray& parts, + const RbxRay& unitMouseRay, + const ContactManager& contactManager, + bool snapToGrid = true); + + static bool hitObject(const PartArray& parts, + const RbxRay& unitMouseRay, + const ContactManager& contactManager, + Vector3& hit, + bool snapToGrid = true); + + static bool anyPartAlive(const PartArray& parts); + + // replacement for PVInstance::getPrimitives(); + + static void getPrimitives2(shared_ptr instance, std::vector& primitives) { + getPrimitives(instance.get(), primitives); + } + + static void getPrimitives(const Instance* instance, std::vector& primitives); + + static void getPrimitivesConst(const Instance* instance, std::vector& primitives); + + static Vector3 safeMoveYDrop(const PartArray& parts, const Vector3& tryDrag, ContactManager& contactManager, const float customPlaneHeight = Dragger::groundPlaneDepth() ); + static Vector3 toGrid(const Vector3 &point, const Vector3& grid = Vector3::zero()); + static Vector3 toLocalGrid(const Vector3& deltaIn); + static Vector3 getGrid(); + + }; +} // namespace diff --git a/App/tool/Dragger.h b/App/tool/Dragger.h new file mode 100644 index 0000000..66d1eaa --- /dev/null +++ b/App/tool/Dragger.h @@ -0,0 +1,190 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once +#include "Util/G3DCore.h" +#include "Util/Extents.h" +#include +#include + +namespace RBX { + + class PVInstance; + class Primitive; + class ContactManager; + class PartInstance; + + class Dragger { + private: + static void primitivesFromInstances(const std::vector& pvInstances, + G3D::Array& primitives); + + // Intersection tests + static bool intersectingWorldOrOthers( const G3D::Array& primitives, + ContactManager& contactManager, + const float bottomPlaneHeight); + + static void movePrimitives( const G3D::Array& primitives, + const Vector3& delta, + bool snapToWorld = true); + + static void movePrimitivesDelta(const G3D::Array& primitives, + const Vector3& delta, + Vector3& movedSoFar); + + static bool movePrimitivesGoal( const G3D::Array& primitives, + const Vector3& goal, + Vector3& movedSoFar, + bool snapToWorld = true); + + static void safePlaceAlongLine( const G3D::Array& primitives, + const Vector3& startMove, + const Vector3& endMove, + Vector3& movedSoFar, + ContactManager& contactManager, + bool snapToWorld = true); + + static void searchFine( const G3D::Array primitives, + Vector3& movedSoFar, + ContactManager& contactManager, + const float bottomPlaneHeight, + Vector3 insidePosition, + Vector3 outsidePosition); + + static void searchUpGross( const G3D::Array& primitives, + Vector3& movedSoFar, + ContactManager& contactManager, + const float bottomPlaneHeight); + + static void searchDownGross( const G3D::Array& primitives, + Vector3& movedSoFar, + ContactManager& contactManager, + const float bottomPlaneHeight); + + static bool intersectingGroundPlane(const G3D::Array& primitives, float yHeight); + + static Vector3 safeMoveYDrop_EXT( const G3D::Array& primitives, + const Vector3& tryMove, + ContactManager& contactManager, + const float customPlaneHeight = groundPlaneDepth()); + + static void searchUpGross_EXT( std::vector &primExtents, + const G3D::Array& primitives, + const boost::unordered_set &ignorePrimitives, + ContactManager& contactManager, + const float bottomPlaneHeight, + Vector3& movedSoFar); + + static void searchDownGross_EXT( std::vector &primExtents, + const G3D::Array& primitives, + const boost::unordered_set &ignorePrimitives, + ContactManager& contactManager, + const float bottomPlaneHeight, + Vector3& movedSoFar); + + static void searchUpFine_EXT( std::vector &primExtents, + const G3D::Array& primitives, + const boost::unordered_set &ignorePrimitives, + ContactManager& contactManager, + const float bottomPlaneHeight, + Vector3& movedSoFar); + + static void searchDownFine_EXT( std::vector &primExtents, + const G3D::Array& primitives, + const boost::unordered_set &ignorePrimitives, + ContactManager& contactManager, + const float bottomPlaneHeight, + Vector3& movedSoFar); + + static bool intersectingWorldOrOthers_EXT( std::vector &primExtents, + const G3D::Array& primitives, + const boost::unordered_set &ignorePrimitives, + ContactManager& contactManager, + const float bottomPlaneHeight, + const Vector3& movedSoFar); + + static bool intersectingGroundPlane_EXT( const std::vector& primExtents, + const G3D::Array& primitives, + const float yHeight, + const Vector3& movedSoFar); + + static bool isIntersecting(const Primitive* prim1, const CoordinateFrame &cFrame1, const Primitive* prim2, const CoordinateFrame &cFrame2); + + static bool checkBallPolyIntersection(const Primitive* ballPrim, const CoordinateFrame &ballCFrame, const Primitive* polyPrim, const CoordinateFrame &polyCFrame); + static bool checkBallBallIntersection(const Primitive* ballPrim1, const CoordinateFrame &ballCFrame1, const Primitive* ballPrim2, const CoordinateFrame &ballCFrame2); + static bool checkPolyPolyIntersection(const Primitive* polyPrim1, const CoordinateFrame &polyCFrame1, const Primitive* polyPrim2, const CoordinateFrame &polyCFrame2); + + static void moveExtents(std::vector &primExtents, const Vector3& delta); + static void moveExtentsDelta(std::vector &primExtents, const Vector3& delta, Vector3& movedSoFar); + + public: + static const Vector3& dragSnap() { + static Vector3 v(1.0f, 0.1f, 1.0f); + return v; + } + // Physics automatically removes parts that fall lower than -500. + // We'll allow dragging, moving, resizing down to -400. + static float maxDragDepth() { return -400.0f; } + static float groundPlaneDepth() { return 0.0f; } + + // Moves up as necessary for no overlap + static Vector3 safeMoveNoDrop( const G3D::Array& primitives, + const Vector3& tryMove, + ContactManager& contactManager); + + // Floating - move down; Intersecting - move up + static Vector3 safeMoveYDrop( const G3D::Array& primitives, + const Vector3& tryMove, + ContactManager& contactManager, + const float customPlaneHeight = groundPlaneDepth() ); + + // Move along line - quickly find farthest safe move + static Vector3 safeMoveAlongLine( const G3D::Array& primitives, + const Vector3& tryMove, + ContactManager& contactManager, + const float customPlaneHeight = groundPlaneDepth(), + bool snapToWorld = true ); + + static Vector3 safeRotateAlongLine( const G3D::Array& primitives, + const Vector3& tryMove, + ContactManager& contactManager); + + // Rotate around a grid point, then find a safe place + static void safeRotate( const G3D::Array& primitives, + const Matrix3& rotate, + ContactManager& contactManager); + + static void safeRotate2( const G3D::Array& primitives, + const Matrix3& rotate, + ContactManager& contactManager); + + static Extents computeExtents(const std::vector& primitives); + + static Extents computeExtentsRelative(const std::vector& primitives, CoordinateFrame& relativeFrame); + + static Extents computeExtentsRelative(const G3D::Array& primitives, CoordinateFrame& relativeFrame); + + static PartInstance* computePrimaryPart(const std::vector& primitives); + + static PartInstance* computePrimaryPart(const G3D::Array& primitives); + + // Intersection tests + static bool intersectingWorldOrOthers( PartInstance& partInstance, + ContactManager& contactManager, + const float tolerance, + const float bottomPlaneHeight); + + static bool intersectingWorldOrOthers( const G3D::Array& primitives, + ContactManager& contactManager, + const float tolerance, + const float bottomPlaneHeight); + + // ToDo::Deprecate Array Version + static Extents computeExtents(const G3D::Array& primitives); + + static Extents computeExtents(const std::vector& instances); + + + + }; + +} // namespace \ No newline at end of file diff --git a/App/tool/DropTool.h b/App/tool/DropTool.h new file mode 100644 index 0000000..741ba2a --- /dev/null +++ b/App/tool/DropTool.h @@ -0,0 +1,31 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" + +/* + // End user tools + + // Drop tool is like a drag tool, except it knows how to cancel itself, and has a different behavior on mouseUp + +*/ + +namespace RBX { + + class Workspace; + class PartInstance; + class PVInstance; + + class DropTool + { + public: + static shared_ptr createDropTool(const Vector3& hitWorld, + const std::vector& dragInstances, + Workspace* workspace, + shared_ptr selectIfNoDrag, + bool suppressPartsAlign = false); + + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/GameTool.h b/App/tool/GameTool.h new file mode 100644 index 0000000..3602ce2 --- /dev/null +++ b/App/tool/GameTool.h @@ -0,0 +1,34 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" + +namespace RBX { + + extern const char* const sGameTool; + class GameTool : public Named + { + private: + std::string cursor; + + bool draggablePart(const PartInstance* part, const Vector3& hitPoint) const; + + ///////////////////////////////////////////////////////// + // MouseCommand + // + /*override*/ void onMouseHover(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + /*override*/ void onMouseIdle(const shared_ptr& inputObject); + /*override*/ const std::string getCursorName() const {return cursor;} + + /*override*/ bool drawConnectors() const {return true;} // default mouse command no draw connectors + + public: + GameTool(Workspace* workspace); + ~GameTool(); + + /*override*/ shared_ptr isSticky() const {return Creatable::create(workspace);} + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/GrabTool.h b/App/tool/GrabTool.h new file mode 100644 index 0000000..525686b --- /dev/null +++ b/App/tool/GrabTool.h @@ -0,0 +1,32 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" + +namespace RBX { + + extern const char* const sGrabTool; + class GrabTool : public Named + { + private: + std::string cursor; + + ///////////////////////////////////////////////////////// + // MouseCommand + // + /*override*/ void onMouseIdle(const shared_ptr& inputObject); + /*override*/ void onMouseHover(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + /*override*/ const std::string getCursorName() const {return cursor;} + + /*override*/ bool drawConnectors() const {return true;} // default mouse command no draw connectors + + public: + GrabTool(Workspace* workspace); + ~GrabTool(); + + /*override*/ shared_ptr isSticky() const {return Creatable::create(workspace);} + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/GroupDragTool.h b/App/tool/GroupDragTool.h new file mode 100644 index 0000000..830f5bf --- /dev/null +++ b/App/tool/GroupDragTool.h @@ -0,0 +1,44 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" +#include "Tool/DragUtilities.h" + +namespace RBX { + + class MegaDragger; + + extern const char* const sGroupDragTool; + class GroupDragTool : public Named + { + protected: + std::auto_ptr megaDragger; + Vector2 downPoint; + bool dragging; + Vector3 lastHit; + + /*override*/ bool drawConnectors() const {return true;} // default mouse command no draw connectors + + public: + ///////////////////////////////////////////////////////// + // MouseCommand + // + /*override*/ shared_ptr onKeyDown(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + /*override*/ void onMouseIdle(const shared_ptr& inputObject); + /*override*/ void onMouseMove(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseUp(const shared_ptr& inputObject); + /*override*/ const std::string getCursorName() const { + return dragging ? "GrabRotateCursor" : "DragCursor"; + } + + GroupDragTool( PartInstance* mousePart, + const Vector3& hitPointWorld, + const PartArray& partArray, + Workspace* workspace); + + ~GroupDragTool(); + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/GroupDropTool.h b/App/tool/GroupDropTool.h new file mode 100644 index 0000000..e32fd04 --- /dev/null +++ b/App/tool/GroupDropTool.h @@ -0,0 +1,43 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Tool/GroupDragTool.h" +#include "Tool/ICancelableTool.h" +#include "Tool/DragUtilities.h" + +namespace RBX { + + class MegaDragger; + + extern const char* const sGroupDropTool; + class GroupDropTool + : public Named + , public ICancelableTool + { + private: + typedef Named Super; + public: + ///////////////////////////////////////////////////////// + // MouseCommand + // + ///*override*/ void onMouseDelta(const shared_ptr& inputObject); + /*override*/ shared_ptr onKeyDown(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseUp(const shared_ptr& inputObject); + /*override*/const std::string getCursorName() const {return MouseCommand::isAdvArrowToolEnabled() ? "advClosed-hand" : "DropCursor";} + + ///////////////////////////////////////////////////////// + // ICancelableTool + // + /*override*/ shared_ptr onCancelOperation(); + + GroupDropTool( PartInstance* mousePart, + const PartArray& partArray, + Workspace* workspace, + bool suppressPartsAlign = false); + + ~GroupDropTool(); + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/HammerTool.h b/App/tool/HammerTool.h new file mode 100644 index 0000000..c36a003 --- /dev/null +++ b/App/tool/HammerTool.h @@ -0,0 +1,27 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" + +namespace RBX { + + extern const char* const sHammerTool; + class HammerTool : public Named + { + private: + typedef Named Super; + shared_ptr hammerPart; + + /*ovverrid*/ void onMouseIdle(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + /*override*/ void render3dAdorn(Adorn* adorn); + /*override*/ const std::string getCursorName() const; + + public: + HammerTool(Workspace* workspace); + ~HammerTool(); + /*override*/ shared_ptr isSticky() const {return Creatable::create(workspace);} + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/ICancelableTool.h b/App/tool/ICancelableTool.h new file mode 100644 index 0000000..09b7071 --- /dev/null +++ b/App/tool/ICancelableTool.h @@ -0,0 +1,18 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" + +namespace RBX +{ + class ICancelableTool + { + + public: + virtual shared_ptr onCancelOperation() = 0; + + }; + + +} //namespace \ No newline at end of file diff --git a/App/tool/LuaDragTool.h b/App/tool/LuaDragTool.h new file mode 100644 index 0000000..d90167b --- /dev/null +++ b/App/tool/LuaDragTool.h @@ -0,0 +1,41 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" +#include "Tool/LuaDragger.h" +#include + +namespace RBX { + + class PartInstance; + + extern const char* const sLuaDragTool; + class LuaDragTool : public Named + { + private: + boost::shared_ptr luaDragger; + boost::weak_ptr selectIfNoDrag; + + ///////////////////////////////////////////////////////// + // MouseCommand + // + /*override*/ void onMouseIdle(const shared_ptr& inputObject); + /*override*/ void onMouseMove(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseUp(const shared_ptr& inputObject); + /*override*/ const std::string getCursorName() const; + /*override*/ shared_ptr onKeyDown(const shared_ptr& inputObject); + + public: + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + + LuaDragTool( PartInstance* mousePart, + const Vector3& hitPointWorld, + const std::vector >& partArray, + Workspace* workspace, + shared_ptr selectIfNoDrag); + + ~LuaDragTool(); + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/LuaDragger.h b/App/tool/LuaDragger.h new file mode 100644 index 0000000..fdd3452 --- /dev/null +++ b/App/tool/LuaDragger.h @@ -0,0 +1,73 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Tree/Instance.h" + +namespace RBX { + + class Joint; + class PartInstance; + class ContactManager; + class RunDragger; + + extern const char* const sLuaDragger; + + class LuaDragger + : public DescribedCreatable + { + private: + typedef enum {NO_PARTS, MOUSE_DOWN, DRAGGING, MOUSE_UP_DRAGGED, MOUSE_UP_NO_DRAG} DragPhase; + DragPhase dragPhase; + + std::vector > jointsIMade; + weak_ptr rootPart; + std::auto_ptr runDragger; // only if we have one part + + float hitPointHeight; + + typedef std::vector > WeakParts; + WeakParts dragParts; + weak_ptr mousePart; + Vector3 pointOnMousePart; + + void tryStartDragging(const RbxRay& unitMouseRay); + void startDragging(); + void doDrag(const RbxRay& unitMouseRay); + bool getSnapHitPoint(PartInstance* part, const RbxRay& unitMouseRay, Vector3& hitPoint); + ContactManager& getContactManager(PartInstance* partInstance); + + const float breakFreeDistance() {return 1.5f;} // distance in studs at the mouse down point before movement + + void addPart(shared_ptr part); + /*override*/ bool askSetParent(const Instance* instance) const { return false; } + + public: + LuaDragger(); + ~LuaDragger(); + + void mouseDownPublic(shared_ptr _mousePart, + Vector3 _pointOnMousePart, + shared_ptr _dragParts); + + void mouseDown( shared_ptr _mousePart, + const Vector3& _pointOnMousePart, + const std::vector > _dragParts); + + void mouseMove(RbxRay mouseRay); // inefficient, but easier to have just one version + + void mouseUp(); + + const WeakParts& getParts() {return dragParts;} + + void rotateOnSnapFace(Vector3::Axis, const Matrix3& rotMatrix); + void axisRotate(Vector3::Axis axis); + + bool isDragging() const {return dragPhase == DRAGGING;} + + bool didDrag() const {return dragPhase == MOUSE_UP_DRAGGED;} + + void alignPartToGrid( void ); + }; + +} // namespace RBX diff --git a/App/tool/MegaDragger.h b/App/tool/MegaDragger.h new file mode 100644 index 0000000..4fe7805 --- /dev/null +++ b/App/tool/MegaDragger.h @@ -0,0 +1,100 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/Object.h" +#include "Util/G3DCore.h" +#include "Tool/DragUtilities.h" +#include "Tool/DragTypes.h" +//#include + +namespace RBX { + class Primitive; + class PVInstance; + class PartInstance; + class RootInstance; + class ContactManager; + class InputObject; + + class MegaDragger + { + private: + weak_ptr mousePart; + PartArray dragParts; + + // Join/unjoin stuff + bool joined; + DRAG::JoinType joinType; + + // Workspace stuff + RootInstance* rootInstance; + ContactManager& contactManager; + + bool moveSafePlaceAlongLine(const Vector3& tryDrag); + + public: + MegaDragger(PartInstance* mousePartPtr, + const std::vector& pvInstances, + RootInstance* rootInstance, + DRAG::JoinType joinType = DRAG::UNJOIN_JOIN); + + MegaDragger(PartInstance* mousePartPtr, + const PartArray& partArray, + RootInstance* rootInstance, + DRAG::JoinType joinType = DRAG::UNJOIN_JOIN); + + + + ~MegaDragger(); + + void setToSelection(const Workspace* workspace); + + // Drag cycle - start, continue (before every move), finish + void startDragging(); + void continueDragging(); // every drag step/move/idle + void finishDragging(); + + // Inquiry + bool mousePartAlive(); + bool anyDragPartAlive(); + + // Part Dragger + weak_ptr getMousePart() { + RBXASSERT(!mousePart.expired()); + return mousePart; + } + + // Group Dragger + void alignAndCleanParts(); + void cleanParts(); + Vector3 hitObjectOrPlane(const shared_ptr& inputObject); // ignore the drag parts - find a hit point with the world + Vector3 safeMoveYDrop(const Vector3& tryDrag); // 1. Go directly to new location, 2. Moves down until collision - if necessary, moves up + + // Axis Tools + Vector3 safeMoveAlongLine(const Vector3& tryDrag, bool snapToWorld = true); + Vector3 safeRotateAlongLine(const Vector3& tryDrag); + + Vector3 safeMoveAlongLine2(const Vector3& tryDrag, bool& out_isCollided); + Vector3 safeRotateAlongLine2(const Vector3& tryDrag, const float &angle); + + Vector3 safeMoveToMinimumHeight(float yValue); + + /** + * Rotates drag parts + * @param rotMatrix rotation matrix to apply + * @param respectCollisions if true then check for collision will be done + in case of collisions no rotation will be performed + * @return Matrix after rotation + */ + Matrix3 rotateDragParts(const Matrix3& rotMatrix, bool respectCollisions); + + bool moveAlongLine(const Vector3& tryDrag); + + // General Placement + Vector3 safeMoveNoDrop(const Vector3& tryDrag); // On initial collision - moves up + bool safeRotate(const Matrix3& rotMatrix); + void removeParts(); + private: + void getPartsForDrag(G3D::Array& primitives); + }; +} // namespace diff --git a/App/tool/MoveResizeJoinTool.h b/App/tool/MoveResizeJoinTool.h new file mode 100644 index 0000000..1558a9f --- /dev/null +++ b/App/tool/MoveResizeJoinTool.h @@ -0,0 +1,74 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Tool/ToolsArrow.h" +#include "AppDraw/HandleType.h" +#include "Util/NormalId.h" +#include "Tool/DragTypes.h" + + +namespace RBX { + + extern const char* const sMoveResizeJoinTool; + class MoveResizeJoinTool : public Named + { + private: + typedef Named Super; + void findTargetPV(const shared_ptr& inputObject); + void capturedDrag(float axisDelta); + float moveIncrement(void); + int smallGridBoxesPerStud(void); + + /*override*/ bool drawConnectors() const {return true;} // default mouse command no draw connectors + + static weak_ptr scalingPart; + + protected: + + bool resizeFloat(shared_ptr part, NormalId localNormalId, float amount, bool checkIntersection); + bool advResizeImpl(shared_ptr part, NormalId localNormalId, float amount, bool checkIntersection); + + // IAdornable + /*override*/ void render3dAdorn(Adorn* adorn); + /*override*/ void render2d(Adorn* adorn); + + // Tool + /*override*/ void onMouseIdle(const shared_ptr& inputObject); + /*override*/ void onMouseHover(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + /*override*/ void onMouseMove(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseUp(const shared_ptr& inputObject); + /*override*/ const std::string getCursorName() const {return cursor;} + /*override*/ shared_ptr onKeyDown(const shared_ptr& inputObject); + /*override*/ void setCursor(std::string newCursor) {cursor = newCursor;} + + weak_ptr targetPV; + + bool overHandle; + NormalId localNormalId; + Vector3 hitPointGrid; + Vector2int16 down; + int movePerp; + bool dragging; + CoordinateFrame origPartPosition; + Vector3 origPartSize; + std::string cursor; + float origPartTransparency; + + public: + MoveResizeJoinTool(Workspace* workspace) : + Named(workspace), + overHandle(false), + movePerp(0), + dragging(false), + origPartSize(0.0f, 0.0f, 0.0f), + cursor("advCursor-default"), + origPartTransparency(0.0f) + {} + /*override*/ shared_ptr isSticky() const {return Creatable::create(workspace);} + + static void setSelection(shared_ptr oldSelection, shared_ptr newSelection); + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/NullTool.h b/App/tool/NullTool.h new file mode 100644 index 0000000..2df73c4 --- /dev/null +++ b/App/tool/NullTool.h @@ -0,0 +1,55 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" + +namespace RBX { + + namespace Network { + class Player; + } + + // Super tool - combines click to move, etc. + extern const char* const sNewNullTool; + class NewNullTool : public Named + { + private: + typedef Named Super; + std::string cursor; + bool hasWaypoint; + Vector3 waypoint; + + bool isInFirstPerson(); + void getIndicatedPart(const shared_ptr& inputObject, const bool& clickEvent, + PartInstance** instance, bool* clickable, Vector3* waypoint); + ///////////////////////////////////////////////////////// + // MouseCommand + // + /*override*/ void onMouseIdle(const shared_ptr& inputObject); + /*override*/ void onMouseHover(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + /*override*/ shared_ptr onRightMouseDown(const shared_ptr& inputObject); + /*override*/ const std::string getCursorName() const {return cursor;} + /*override*/ shared_ptr isSticky() const {return Creatable::create(workspace);} + /*override*/ shared_ptr onMouseUp(const shared_ptr& inputObject) { + releaseCapture(); + return shared_from(this); + } + /*override*/ shared_ptr onRightMouseUp(const shared_ptr& inputObject); + + ///////////////////////////////////////////////////////// + // IAdornable + // + /*override*/ bool shouldRender3dAdorn() const {return true;} + /*override*/ void render3dAdorn(Adorn* adorn); + + void updateClickDetectorHover(const shared_ptr& inputObject); + + public: + NewNullTool(Workspace* workspace); + ~NewNullTool(); + + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/PartDragTool.h b/App/tool/PartDragTool.h new file mode 100644 index 0000000..c1c03f2 --- /dev/null +++ b/App/tool/PartDragTool.h @@ -0,0 +1,50 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" + +namespace RBX { + + class RunDragger; + class MegaDragger; + + extern const char* const sPartDragTool; + class PartDragTool : public Named + { + private: + typedef Named Super; + protected: + std::auto_ptr runDragger; // does snapping + std::auto_ptr megaDragger; // does join / unJoin + shared_ptr selectIfNoDrag; + Vector2 downPoint; + bool dragging; + Vector3 hitWorld; + + /*override*/ bool drawConnectors() const {return true;} // default mouse command no draw connectors + + public: + ///////////////////////////////////////////////////////// + // MouseCommand + // + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + /*override*/ void onMouseIdle(const shared_ptr& inputObject); + /*override*/ void onMouseMove(const shared_ptr& inputObject); + /*override*/ void onMouseDelta(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseUp(const shared_ptr& inputObject); + /*override*/ shared_ptr onKeyDown(const shared_ptr& inputObject); + /*override*/ void render3dAdorn(Adorn* adorn); + /*override*/ const std::string getCursorName() const { + return dragging ? "GrabRotateCursor" : "DragCursor"; + } + + PartDragTool( PartInstance* mousePart, + const Vector3& hitPointWorld, + Workspace* workspace, + shared_ptr selectIfNoDrag); + + ~PartDragTool(); + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/PartDropTool.h b/App/tool/PartDropTool.h new file mode 100644 index 0000000..af82f9d --- /dev/null +++ b/App/tool/PartDropTool.h @@ -0,0 +1,44 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Tool/PartDragTool.h" +#include "Tool/ICancelableTool.h" +#include + +namespace RBX { + + extern const char* const sPartDropTool; + class PartDropTool + : public Named + , public ICancelableTool + { + private: + typedef Named Super; + public: + ///////////////////////////////////////////////////////// + // MouseCommand + // + /*override*/ void onMouseDelta(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + ///*override*/ MouseCommand* onMouseUp(const shared_ptr& inputObject); + /*override*/ shared_ptr onKeyDown(const shared_ptr& inputObject); + /*override*/const std::string getCursorName() const {return MouseCommand::isAdvArrowToolEnabled() ? "advClosed-hand" : "DropCursor";} + + ///////////////////////////////////////////////////////// + // ICancelableTool + // + /*override*/ shared_ptr onCancelOperation(); + + PartDropTool( PartInstance* mousePart, + const Vector3& hitPointWorld, + Workspace* workspace, + shared_ptr selectIfNoDrag); + + ~PartDropTool(); + + private: + Vector3 hitLocal; + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/ResizeTool.h b/App/tool/ResizeTool.h new file mode 100644 index 0000000..b6f1656 --- /dev/null +++ b/App/tool/ResizeTool.h @@ -0,0 +1,55 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Tool/ToolsArrow.h" +#include "AppDraw/HandleType.h" +#include "Util/NormalId.h" + +namespace RBX { + + extern const char* const sResizeTool; + + class ResizeTool : public Named + { + + private: + typedef Named Super; + void findTargetPV(const shared_ptr& inputObject); + void capturedDrag(int axisDelta); + + /*override*/ bool drawConnectors() const {return true;} // default mouse command no draw connectors + + protected: + // IAdornable + /*override*/ void render3dAdorn(Adorn* adorn); + /*override*/ void render2d(Adorn* adorn); + + // Tool + /*override*/ void onMouseHover(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + /*override*/ void onMouseMove(const shared_ptr& inputObject); + /*override*/ shared_ptr onMouseUp(const shared_ptr& inputObject); + /*override*/ const std::string getCursorName() const; + + weak_ptr targetPV; + + bool overHandle; + NormalId localNormalId; + Vector3 hitPointGrid; + Vector2int16 down; + int moveAxis; + int movePerp; + int moveIncrement; + + public: + ResizeTool(Workspace* workspace) : + Named(workspace), + overHandle(false), + moveAxis(0), + movePerp(0) + {} + /*override*/ shared_ptr isSticky() const {return Creatable::create(workspace);} + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/tool/RunDragger.h b/App/tool/RunDragger.h new file mode 100644 index 0000000..dab7f88 --- /dev/null +++ b/App/tool/RunDragger.h @@ -0,0 +1,113 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +//#include "V8World/Joint.h" +#include "V8World/Contact.h" +#include "Util/Object.h" +#include "Util/G3DCore.h" +#include "Util/NormalId.h" +#include +#include "Tool/DragTypes.h" +#include "AppDraw/DrawAdorn.h" +#include "GfxBase/Adorn.h" +#include "GfxBase/IAdornable.h" + + + +namespace RBX { + class Primitive; + class PartInstance; + class Workspace; + + class RunDragger : public IAdornable + { + private: + class SnapInfo { + public: + Primitive* snap; + size_t mySurfaceId; + Vector3 hitWorld; + Vector3 lastHitWorld; + Vector3 lastDragSnap; + SnapInfo() + : snap(NULL) + , mySurfaceId((size_t)-1) + , hitWorld(Vector3::inf()) + , lastHitWorld(Vector3::inf()) + , lastDragSnap(Vector3::inf()) + {} + void updateHitFromSurface(const RbxRay& mouseRay); + void updateSurfaceFromHit(); + float hitOutsideExtents(); + }; + + // Initialized + // Poor man's version - ultimately, change all internally to shared_ptr's + weak_ptr dragPart; + weak_ptr snapPart; + + Workspace* workspace; + Primitive* drag; + Vector3 dragPointLocal; + Matrix3 dragOriginalRotation; + + // Carry forward data + SnapInfo snapInfo; + + // Set on every snap() call + RbxRay mouseRay; + + // Computed every snap() call + NormalId dragSurface; // computed + // Angled surface testing... + size_t myDragSurfaceId; // computed + Vector3 dragHitLocal; // computed + + // modes + + SnapInfo createSnapSurface(Primitive* snap, G3D::Array* ignore = NULL); + bool moveDragPart(); + bool snapDragPart(); + void snapRotatePart(); + + void findSafeY(); + bool notTried(Primitive* check, const G3D::Array& tried); + bool adjacent(Primitive* p0, Primitive* p1); + SnapInfo rayHitsPart(const G3D::Array& triedSnap, bool forceAdjacent); + SnapInfo bestProximatePart(const G3D::Array& triedSnap, + Contact::ProximityTest proximityTest); + bool fallOffEdge(); + bool fallOffPart(bool& snapped); + bool colliding(); + bool rayHitsCloserPart(); + bool tooCloseToCamera(); + + SnapInfo findSnap(const G3D::Array& triedSnap); + void findNoSnapPosition(const CoordinateFrame& original); + + void snapInfoFromSnapPart(); + void snapPartFromSnapInfo(); + + public: + RunDragger(); + ~RunDragger(); + void init( Workspace* _workspace, + weak_ptr _dragPart, + const Vector3& _dragPointWorld); + void initLocal( Workspace* _workspace, + weak_ptr _dragPart, + const Vector3& _dragPointLocal); + + bool snap(const RbxRay& mouseRay); + + void rotatePartAboutSnapFaceAxis( Vector3::Axis axis, const float& angleInRads ); + void rotatePart90DegAboutSnapFaceAxis( Vector3::Axis axis ); + CoordinateFrame getSnapSurfaceCoord(); + + // Note: these static functions will NOT update dragOriginalRotation + static void turnUpright(PartInstance* part); + static void rotatePart(PartInstance* part); + static void tiltPart(PartInstance* part, const CoordinateFrame& camera); + }; +} // namespace \ No newline at end of file diff --git a/App/tool/ToolsArrow.h b/App/tool/ToolsArrow.h new file mode 100644 index 0000000..8994238 --- /dev/null +++ b/App/tool/ToolsArrow.h @@ -0,0 +1,161 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8DataModel/MouseCommand.h" +#include "V8DataModel/ManualJointHelper.h" + +namespace RBX { + +class PartInstance; +class Decal; + +///////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////// +// +// ARROW + +#define STUDIO_CAMERA_CONTROL_SHORTCUTS 1 + + class ArrowToolBase : public MouseCommand + { + private: + + typedef MouseCommand Super; + + bool altKeyDown; + + protected: + + Decal* findDecal(PartInstance* p, const shared_ptr& inputObject); + + virtual void onMouseIdle(const shared_ptr& inputObject); + virtual void onMouseHover(const shared_ptr& inputObject); + virtual shared_ptr onMouseDown(const shared_ptr& inputObject); + virtual const std::string getCursorName() const; + virtual shared_ptr onPeekKeyDown(const shared_ptr& inputObject); + virtual void render3dAdorn(Adorn* adorn); + + void renderHoverOver(Adorn* adorn,bool drillDownOnly = true); + + PartInstance* overInstance; + + public: + + ArrowToolBase(Workspace* workspace) + : MouseCommand(workspace), + overInstance(NULL), + altKeyDown(false) + { + FASTLOG1(FLog::MouseCommandLifetime, "ArrowTool created: %p", this); + } + + virtual ~ArrowToolBase() + { + FASTLOG1(FLog::MouseCommandLifetime, "ArrowTool destroyed: %p", this); + } + + static bool showDraggerGrid; + }; + +///////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////// +// +// ADVANCED ARROW (parent class for all advanced mouse manipulation + + class AdvArrowToolBase : public ArrowToolBase + { + private: + ManualJointHelper manualJointHelper; + typedef ArrowToolBase Super; + protected: + typedef std::map, float> PartsTransparencyCollection; + static PartsTransparencyCollection originalPartsTransparency; + public: + AdvArrowToolBase(Workspace* workspace) : ArrowToolBase(workspace), manualJointHelper(workspace) + {} + virtual ~AdvArrowToolBase() {} + + enum JointCreationMode + { + WELD_ALL = 0, + SURFACE_JOIN_ONLY = 1, + NO_JOIN = 2 + }; + + virtual shared_ptr onMouseDown(const shared_ptr& inputObject); + virtual void onMouseMove(const shared_ptr& inputObject); + virtual shared_ptr onMouseUp(const shared_ptr& inputObject); + virtual const std::string getCursorName() const; + shared_ptr onKeyDown(const shared_ptr& inputObject); + + void determineManualJointConditions(void); + + static DRAG::DraggerGridMode advGridMode; + static bool advManualJointMode; + static DRAG::ManualJointType advManualJointType; + static bool advManipInProgress; + + static bool advCollisionCheckMode; + static bool advLocalTranslationMode; + static bool advLocalRotationMode; + static bool advCreateJointsMode; + + static void restoreSavedPartsTransparency(); + static void savePartTransparency(shared_ptr part); + + static AdvArrowToolBase::JointCreationMode getJointCreationMode(); + + virtual void getSelectedTargetPrimitives(std::vector& targetPrims) {} + virtual void setCursor(std::string cursor) {} + }; + + extern const char* const sAdvArrowTool; + class AdvArrowTool : public Named + { + public: + AdvArrowTool(Workspace* workspace) : Named(workspace) + { + } + + /*override*/ shared_ptr isSticky() const {return Creatable::create(workspace);} + + virtual void setCursor(std::string cursor) {} + + }; + +///////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////// +// +// BOX SELECT + + extern const char* const sBoxSelectCommand; + class BoxSelectCommand : public Named + { + private: + typedef Named Super; + ServiceClient selection; + bool reverseSelecting; + Vector2int16 mouseDownView; + Vector2int16 mouseCurrentView; + std::set< shared_ptr > previousItemsInBox; + + void getMouseInstances(std::set< shared_ptr >& instances, + const shared_ptr& inputObject, + const Rect2D& selectBox, const Camera* camera, + Instance* currentInstance); + + void selectAnd(const std::set< shared_ptr >& newItemsInBox); + + void selectReverse(const std::set< shared_ptr >& newItemsInBox); + + public: + /*override*/ shared_ptr onMouseDown(const shared_ptr& inputObject); + /*override*/ void onMouseMove(const shared_ptr& inputObject); + /*override*/ void render2d(Adorn* adorn); + + BoxSelectCommand(Workspace* workspace); + ~BoxSelectCommand(); + }; + +} // namespace RBX diff --git a/App/util/Action.h b/App/util/Action.h new file mode 100644 index 0000000..001c5d7 --- /dev/null +++ b/App/util/Action.h @@ -0,0 +1,25 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +//#include "Util/SoundWorld.h" +#include + + +namespace RBX { + + class Action + { + public: + enum ActionType { NO_ACTION = 0, + PAUSE_ACTION, + LOSE_ACTION, + DRAW_ACTION, + WIN_ACTION, + NUM_ACTION_TYPES }; + private: + Action(); + +}; + +} // namespace RBX \ No newline at end of file diff --git a/App/util/Analytics.h b/App/util/Analytics.h new file mode 100644 index 0000000..1df2ca5 --- /dev/null +++ b/App/util/Analytics.h @@ -0,0 +1,203 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "FastLog.h" +#include "RbxFormat.h" + +DYNAMIC_FASTFLAG(InfluxDb09Enabled) + +namespace RBX { + +namespace Analytics { + + void setUserId(int id); + void setPlaceId(int id); + void setAppVersion(const std::string& version); + void setLocation(const std::string& loc); + void setReporter(const std::string& rep); + +namespace EphemeralCounter +{ + void reportStats(const std::string& category, float value, bool blocking = false); + void reportCountersCSV(const std::string& counterNamesCSV, bool blocking = false); + void reportCounter(const std::string& counterName, int amount, bool blocking = false); +} + +namespace GoogleAnalytics +{ + // Allow for easy initialization based on a lottery number. + // Calls setCanUseAnalytics and init. + void lotteryInit(const std::string &accountPropertyID, int lotteryThreshold, const std::string& productName = "", int robloxAnalyticsLottery = -1, const std::string &sessionKey = "sessionID="); + + // Must be called before using the singleton. + void init(const std::string &accountPropertyID, const std::string& productName = ""); + + bool getCanUse(); + void setCanUse(); + + void sendEventRoblox(const char* category, const char* action = "custom", const char* label = "none", int value = 0, bool sync = false); + + void trackEvent(const char *category, const char *action = "custom", const char *label = "none", int value = 0, bool sync = false); + void trackEventWithoutThrottling(const char *category, const char *action = "custom", const char *label = "none", int value = 0, bool sync = false); + void trackUserTiming(const char *category, const char *variable, int milliseconds, const char *label = "none", bool sync = false); + + const std::string& getSessionId(); + +} // namespace GoogleAnalytics + + +namespace InfluxDb { + + struct Point + { + std::string name; + std::string json; + + Point(const std::string& name_, const rapidjson::Value& value) : name(name_) + { + using namespace rapidjson; + + switch (value.GetType()) + { + case kNullType: + json = "null"; + break; + case kFalseType: + json = "false"; + break; + case kTrueType: + json = "true"; + break; + case kObjectType: + case kArrayType: + throw std::runtime_error("Arrays and objects are not valid value types."); + break; + case kStringType: + if (DFFlag::InfluxDb09Enabled) + { + // we must escape double quotes + std::string sval = value.GetString(); + boost::replace_all(sval, "\"", "\\\""); + json = std::string("\"") + sval + "\""; + } + else + { + json = std::string("\"") + value.GetString() + "\""; + } + break; + case kNumberType: + { + const rapidjson::Value& v = value; + + std::stringstream ss; + if (v.IsDouble()) + { + ss << v.GetDouble(); + } + else if (v.IsInt()) + { + ss << v.GetInt(); + if (DFFlag::InfluxDb09Enabled) + { + ss << 'i'; // signals integer type + } + } + else if (v.IsInt64()) + { + ss << v.GetInt64(); + if (DFFlag::InfluxDb09Enabled) + { + ss << 'i'; // signals integer type + } + } + else if (v.IsUint()) + { + ss << v.GetUint(); + if (DFFlag::InfluxDb09Enabled) + { + ss << 'i'; // signals integer type + } + } + else if (v.IsUint64()) + { + ss << v.GetUint64(); + if (DFFlag::InfluxDb09Enabled) + { + ss << 'i'; // signals integer type + } + } + else + { + throw std::runtime_error("Unknown number type."); + } + + ss >> json; + } + break; + default: + throw std::runtime_error("Unknown rapidjson value type."); + break; + } + } + + bool operator==(const Point& other) const { + return this->name == other.name; + } + }; + std::size_t hash_value(const Point& p); + + void init(); + void reportPoints(const std::string& resource, const boost::unordered_set& points, int throttleHundredthsPercentage, bool blocking = false, const std::string& userIdOverride = ""); + void reportPointsV2(const std::string& resource, const boost::unordered_set& points, int throttleHundredthsPercentage, bool blocking = false, const std::string& userIdOverride = ""); + + class Points + { + boost::unordered_set pointList; + std::string userIdOverride; + + public: + Points() {} + ~Points() {} + + void setUserIdOverride(const int id) + { + userIdOverride = RBX::format("%d", id); + } + + void addPoint(const std::string& name, const rapidjson::Value& value, bool override = false) + { + Point newPoint = Point(name, value); + std::pair::iterator, bool> res = pointList.insert(newPoint); + + if (override && !res.second) + { + pointList.erase(res.first); + pointList.insert(newPoint); + } + } + + void report(const std::string& resource, int throttleHundredthsPercentage, bool blocking = false) + { + if (!pointList.empty()) + { + if (DFFlag::InfluxDb09Enabled) + reportPointsV2(resource, pointList, throttleHundredthsPercentage, blocking, userIdOverride); + else + reportPoints(resource, pointList, throttleHundredthsPercentage, blocking, userIdOverride); + pointList.clear(); + } + } + + const boost::unordered_set& getPoints() { return pointList; } + }; + + +} // namespace InfluxDb + +} // namespace Analytics + +} // namespace RBX \ No newline at end of file diff --git a/App/util/AnimationId.h b/App/util/AnimationId.h new file mode 100644 index 0000000..c59fbf2 --- /dev/null +++ b/App/util/AnimationId.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Util/ContentId.h" + +namespace RBX { + + class AnimationId : public ContentId + { + public: + AnimationId(const ContentId& id):ContentId(id) {} + AnimationId(const char* id):ContentId(id) {} + AnimationId(const std::string& id):ContentId(id) {} + AnimationId() {} + + bool isActive() const { return toString().substr(0, 9)=="active://"; } + + static AnimationId nullAnimation() { + static AnimationId t; // note - the name in the contentId will get a boost call_once + return t; + } + }; +} \ No newline at end of file diff --git a/App/util/AsyncHttpCache.h b/App/util/AsyncHttpCache.h new file mode 100644 index 0000000..cc52676 --- /dev/null +++ b/App/util/AsyncHttpCache.h @@ -0,0 +1,127 @@ +#pragma once +#include "FastLog.h" +#include "Util/AsyncHttpQueue.h" +#include "rbx/make_shared.h" + +namespace RBX +{ +template +class AsyncHttpCache + :public AsyncHttpQueue +{ +public: + AsyncHttpCache(Instance* owner, boost::function getLocalFile, int threadCount, int cacheSize) + :AsyncHttpQueue(owner, getLocalFile, threadCount) + ,contentCache(cacheSize) + {} + shared_ptr getRequestedUrls() + { + shared_ptr result(rbx::make_shared()); + { + { + boost::mutex::scoped_lock lock(contentCacheMutex); + for (typename ContentCache::List_Iter iter = contentCache.begin(); iter != contentCache.end(); ++iter) + { + ContentId id(iter->first); + if (id.isHttp()) + result->push_back(id.toString()); + } + } + + { + boost::recursive_mutex::scoped_lock lock(requestSync); + std::list< FailedUrl >::const_iterator end = failedUrls.end(); + for (std::list< FailedUrl >::const_iterator iter = failedUrls.begin(); iter!=end; ++iter) + { + result->push_back(iter->url); + } + } + } + return result; + } + + void setCacheSize(int count) + { + boost::mutex::scoped_lock lock(contentCacheMutex); + contentCache.resize(count); + } + + bool findCacheItem(const std::string& id, CachedContent* result) + { + boost::mutex::scoped_lock lock(contentCacheMutex); + return contentCache.fetch(id, result); + } + void removeCacheItem(const std::string& id) + { + boost::mutex::scoped_lock lock(contentCacheMutex); + contentCache.remove(id); + } + void invalidateCacheItemOrFailure(const std::string& id) + { + { + boost::mutex::scoped_lock lock(contentCacheMutex); + contentCache.remove(id); + } + { + boost::recursive_mutex::scoped_lock lock(requestSync); + std::list::iterator found = failedUrls.end(); + for (std::list::iterator itr = failedUrls.begin(); + itr != failedUrls.end() && found == failedUrls.end(); ++itr) + { + if (itr->url == id) + { + found = itr; + } + } + if (found != failedUrls.end()) + failedUrls.erase(found); + } + } + void insertCacheItem(const std::string& id, const CachedContent& result) + { + boost::mutex::scoped_lock lock(contentCacheMutex); + contentCache.insert(id, result); + } + void renameCacheItem(const std::string& id, const std::string& newId) + { + boost::mutex::scoped_lock lock(contentCacheMutex); + CachedContent content; + if (contentCache.fetch(id, &content)) + { + //"rename" the entry. + contentCache.remove(id); + contentCache.insert(newId, content); + } + } + + void clearCache() + { + { + boost::mutex::scoped_lock lock(contentCacheMutex); + contentCache.clear(); + } + { + boost::recursive_mutex::scoped_lock lock(requestSync); + failedUrls.clear(); + } + } + + void printContentNames() + { + boost::mutex::scoped_lock lock(contentCacheMutex); + contentCache.printContentNames(); + } + +protected: + /*override*/ void registerContent(const std::string& url, shared_ptr response, shared_ptr filename) + { + if(Log) FASTLOGS(FLog::HttpQueue, "URL(%s)", url.c_str()); + boost::mutex::scoped_lock lock(contentCacheMutex); + contentCache.insert(url, CachedContent(response, filename)); + } + typedef SizeEnforcedLRUCache ContentCache; + + boost::mutex contentCacheMutex; //synchronizes the contentCache + ContentCache contentCache; +}; +} \ No newline at end of file diff --git a/App/util/AsyncHttpQueue.h b/App/util/AsyncHttpQueue.h new file mode 100644 index 0000000..6202f74 --- /dev/null +++ b/App/util/AsyncHttpQueue.h @@ -0,0 +1,134 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "util/name.h" +#include "rbx/boost.hpp" +#include "rbx/rbxTime.h" +#include "Util/contentid.h" +#include "Util/HeartbeatInstance.h" +#include "Util/LRUCache.h" +#include "Util/ThreadPool.h" +#include "Util/Http.h" +#include "V8Tree/Service.h" + +LOGGROUP(HttpQueue) + +namespace RBX { + class DataModel; + class HttpQueueStatsItem; + +class AsyncHttpQueue + : public boost::enable_shared_from_this + , public boost::noncopyable +{ + shared_ptr statsItem; + +public: + typedef enum { Waiting, Succeeded, Failed } RequestResult; + typedef enum { AsyncInline, AsyncNone, AsyncRead, AsyncWrite} ResultJob; + + typedef boost::function response, shared_ptr exception)> RequestCallback; + + struct CallbackWrapper + { + RequestCallback callback; + ResultJob jobType; + CallbackWrapper(RequestCallback callback, ResultJob jobType) + :callback(callback) + ,jobType(jobType) + {} + }; + + void setThreadPool(int count); + void setCachePolicy(const HttpCache::Policy policy) { cachePolicy = policy; } + + bool isRequestQueueEmpty(); + bool isUrlBad(const std::string& id); + + void asyncRequest(const std::string& id, float priority, RequestCallback* callback, ResultJob jobType, bool ignoreBadRequests=false, const std::string& expectedType = ""); + bool syncRequest(const std::string& id, const std::string& expectedType = ""); + + + static void dispatchGenericCallback(boost::function theCallback, Instance* instance, ResultJob jobType); + static void dispatchCallback(RequestCallback theCallback, Instance* instance, + RequestResult result, boost::shared_ptr data, ResultJob jobType, shared_ptr exception); + + AsyncHttpQueue(Instance* owner,boost::function getLocalFile, int threadCount); + virtual ~AsyncHttpQueue(); + void onHeartbeat(const Heartbeat& heartbeatEvent); + + int getRequestQueueSize() const; + shared_ptr getFailedUrls(); + shared_ptr getRequestQueueUrls(); + + void resetStatsItem(ServiceProvider* provider); + double getAvgTimeInQueue() { return avgTimeInQueue.value(); } + double getAvgRequestCompleteTime() { return avgRequestCompleteTime.value(); } + int getNumSlowRequests() {return numSlowRequests;} + +protected: + virtual void registerContent(const std::string& url, shared_ptr response, shared_ptr filename) + {} + struct Request + { + std::string url; + std::vector callbacks; + float priority; + std::string expectedType; // used in header + boost::shared_ptr http; // keep a handle so we can cancel. + RBX::Time startTime; // the time when this request was issued + bool operator==(const std::string& url) const { return this->url==url; } + }; + + RunningAverage avgTimeInQueue; // in msec + RunningAverage avgRequestCompleteTime; // in msec + int numSlowRequests; + + struct FailedUrl + { + std::string url; + RBX::Time expiration; + FailedUrl(const char* url); + bool expired() const; + }; + + typedef std::list< Request > RequestList; + typedef RequestList::iterator RequestHandle; + + struct AsyncRetryTask + { + double retryTime; + RequestHandle request; + AsyncRetryTask(RequestHandle request , double retryTime) + :request(request), retryTime(retryTime) + {} + }; + + mutable boost::recursive_mutex requestSync; // synchronizes the requestQueue and failedUrls queue, and threadPool + // also used to protect modification of the http shared pointer. + + + RequestList requestQueue; // queue of requested URLs + std::list< FailedUrl > failedUrls; // List of bad URLs + + boost::scoped_ptr threadPool; + + boost::recursive_mutex asyncRetrySync; + std::queue asyncRetryTasks; + + double currentWallTime; + + static void processRequests(boost::weak_ptr httpQueue, RequestHandle request, boost::shared_ptr lock); + void addAsyncRetryTask(RequestHandle request); + + Instance* owner; + boost::function getLocalFile; + + HttpCache::Policy cachePolicy; +}; +} \ No newline at end of file diff --git a/App/util/Average.h b/App/util/Average.h new file mode 100644 index 0000000..0da230c --- /dev/null +++ b/App/util/Average.h @@ -0,0 +1,61 @@ +#pragma once + +// Class that can average 512 prior values using only 9 floats for storage + +#include + +namespace RBX { + + template + class Average { + private: + size_t samples; + size_t tag; + std::vector history; + + public: + Average(size_t samples, T initValue) : samples(samples), tag(0) + { + history.resize(samples, initValue); + } + + void sample(T value, bool advanceBuffer = true) + { + history[tag] = value; + if (advanceBuffer) { + tag = (tag + 1) % samples; + } + } + + T getAverage() const + { + T answer = T(); + for (size_t i = 0; i < samples; ++i) { + answer += history[i]; + } + return answer / static_cast(samples); + } + + size_t size() const { + return samples; + } + + const T& getValue(size_t index) const { + return history[index]; + } + + void resetValues(const T& value) { + for (size_t i = 0; i < samples; ++i) { + history[i] = value; + } + } + + void resetValues(size_t samplesNew, const T& value) { + for (size_t i = 0; i < samples; ++i) { + history[i] = value; + } + history.resize(samplesNew, value); + samples = samplesNew; + } + }; +} // namespace \ No newline at end of file diff --git a/App/util/Axes.h b/App/util/Axes.h new file mode 100644 index 0000000..3cc94e3 --- /dev/null +++ b/App/util/Axes.h @@ -0,0 +1,37 @@ +/* Copyright 2003-2009 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/NormalId.h" + +namespace RBX { + + //A utility class for holding a set of "Faces" associated with an object (top, bottom, left, right, front, back) + class Axes + { + public: + static int axisToMask(Vector3::Axis axis); + static Vector3::Axis normalIdToAxis(NormalId normalId); + static NormalId axisToNormalId(Vector3::Axis axis); + + public: + Axes(int axisMask = 0); + void clear() { axisMask = 0; } + void setAxisByNormalId(NormalId normalId, bool value); + bool getAxisByNormalId(NormalId normalId) const; + void setAxis(Vector3::Axis axis, bool value); + bool getAxis(Vector3::Axis axis) const; + + + + bool operator==(const Axes& other) const { + return axisMask == other.axisMask; + } + bool operator!=(const Axes& other) const { + return axisMask != other.axisMask; + } + + + int axisMask; + }; +} diff --git a/App/util/Base64BinaryInputStream.h b/App/util/Base64BinaryInputStream.h new file mode 100644 index 0000000..e6a8066 --- /dev/null +++ b/App/util/Base64BinaryInputStream.h @@ -0,0 +1,21 @@ +#pragma once + +#include "boost/cstdint.hpp" + +namespace RBX { + +struct Base64BinaryInputStream { +private: + const char* source; + boost::uint16_t buffer; + size_t readableBitsInBuffer; + static unsigned char decode(unsigned char charFromString); + +public: + Base64BinaryInputStream(const char* source); + + // numBitsToRead needs to be >= 1 and <= 8. + void ReadBits(unsigned char* out, size_t numBitsToRead); +}; + +} diff --git a/App/util/Base64BinaryOutputStream.h b/App/util/Base64BinaryOutputStream.h new file mode 100644 index 0000000..2bc9b0e --- /dev/null +++ b/App/util/Base64BinaryOutputStream.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace RBX { + +struct Base64BinaryOutputStream { +private: + static const char* kTranslateToBase64; + + std::ostringstream result; + unsigned char buffer; + size_t bitsUsed; + +public: + + Base64BinaryOutputStream(); + + // NOT A REAL IMPLEMENTATION -- ONLY PRESENT TO SATISFY TEMPLATES + size_t GetNumberOfBytesUsed() const; + + // numBitsToAdd must be >= 0 and <= 8. Only the character immediately + // pointed to by data will be read + void WriteBits(const unsigned char* data, size_t numBitsToAdd); + + // make sure to call this method exactly once: when you will no longer + // call WriteBits again on this object. + void done(std::string* out); +}; + +} diff --git a/App/util/BiMultiMap.h b/App/util/BiMultiMap.h new file mode 100644 index 0000000..856605d --- /dev/null +++ b/App/util/BiMultiMap.h @@ -0,0 +1,65 @@ +#pragma once + +#include "rbx/Debug.h" +#include + +// Poor man's version of a Bi-MultiMap. Restriction is that each "pair" can only be here once +// Assumes we can have many A's, many B's +// Need to re-write with dual indexed multi-set or some other data structure + +namespace RBX { + + template + class BiMultiMap { + public: + typedef std::multimap InternalMap; + typedef typename InternalMap::iterator InternalMapIt; + InternalMap internalMap; + + bool pairInMap(const Left& left, const Right& right) { + InternalMapIt it; + for (it = internalMap.lower_bound(left); it != internalMap.upper_bound(left); ++it) { + if (it->second == right) { + return true; + } + } + return false; + } + + void insertPair(const Left& left, const Right& right) { + RBXASSERT_SLOW(!pairInMap(left, right)); + internalMap.insert(std::make_pair(left, right)); + } + + void removePair(const Left& left, const Right& right) { + RBXASSERT_SLOW(pairInMap(left, right)); + typename InternalMap::iterator it; + for (it = internalMap.lower_bound(left); it != internalMap.upper_bound(left); ++it) { + if (it->second == right) { + internalMap.erase(it); + RBXASSERT(!pairInMap(left, right)); + return; + } + } + RBXASSERT(0); + } + + bool empty() const { + return internalMap.empty(); + } + + bool emptyLeft(const Left& left) const { + return (internalMap.lower_bound(left) == internalMap.upper_bound(left)); + } + + template + inline void visitEachLeft(const Left& left, const Func& func) const { + typename InternalMap::const_iterator it; + for (it = internalMap.lower_bound(left); it != internalMap.upper_bound(left); ++it) { + const Right& right = it->second; + func(left, right); + } + } + + }; +} // namespace RBX \ No newline at end of file diff --git a/App/util/BinaryString.h b/App/util/BinaryString.h new file mode 100644 index 0000000..f613f2b --- /dev/null +++ b/App/util/BinaryString.h @@ -0,0 +1,49 @@ +#pragma once + +#include + +namespace RBX { + +// use this for properties that contain binary data so that they're serialized to XML without roundtrip issues +class BinaryString +{ +public: + BinaryString() + { + } + + explicit BinaryString(const std::string& value) + : internalValue(value) + { + } + + const std::string& value() const + { + return internalValue; + } + + void set(const char* buffer, unsigned int size) + { + internalValue.assign(buffer, size); + } + + bool operator==(const BinaryString& other) const + { + return internalValue == other.internalValue; + } + + bool operator!=(const BinaryString& other) const + { + return internalValue != other.internalValue; + } + + bool operator<(const BinaryString& other) const + { + return internalValue < other.internalValue; + } + +private: + std::string internalValue; +}; + +} diff --git a/App/util/BrickColor.h b/App/util/BrickColor.h new file mode 100644 index 0000000..e8fc3a1 --- /dev/null +++ b/App/util/BrickColor.h @@ -0,0 +1,335 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "G3D/Color3.h" +#include "G3D/Color4.h" +#include "G3D/Color3uint8.h" +#include "G3D/Color4uint8.h" + +#include + +namespace RBX { + + // A collection of official ROBLOX colors + class BrickColor + { + class BrickMap; + public: + enum Number { + brick_1 = 1, + brick_2 = 2, + brick_3 = 3, + brick_5 = 5, + brick_6 = 6, + brick_9 = 9, + brick_11 = 11, + brick_12 = 12, + brick_18 = 18, + brick_21 = 21, + brick_22 = 22, + brick_23 = 23, + brick_24 = 24, + brick_25 = 25, + brick_26 = 26, + brick_27 = 27, + brick_28 = 28, + brick_29 = 29, + brick_36 = 36, + brick_37 = 37, + brick_38 = 38, + brick_39 = 39, + brick_40 = 40, + brick_41 = 41, + brick_42 = 42, + brick_43 = 43, + brick_44 = 44, + brick_45 = 45, + brick_47 = 47, + brick_48 = 48, + brick_49 = 49, + brick_50 = 50, + brick_100 = 100, + brick_101 = 101, + brick_102 = 102, + brick_103 = 103, + brick_104 = 104, + brick_105 = 105, + brick_106 = 106, + brick_107 = 107, + brick_108 = 108, + brick_110 = 110, + brick_111 = 111, + brick_112 = 112, + brick_113 = 113, + brick_115 = 115, + brick_116 = 116, + brick_118 = 118, + brick_119 = 119, + brick_120 = 120, + brick_121 = 121, + brick_123 = 123, + brick_124 = 124, + brick_125 = 125, + brick_126 = 126, + brick_127 = 127, + brick_128 = 128, + brick_131 = 131, + brick_133 = 133, + brick_134 = 134, + brick_135 = 135, + brick_136 = 136, + brick_137 = 137, + brick_138 = 138, + brick_140 = 140, + brick_141 = 141, + brick_143 = 143, + brick_145 = 145, + brick_146 = 146, + brick_147 = 147, + brick_148 = 148, + brick_149 = 149, + brick_150 = 150, + brick_151 = 151, + brick_153 = 153, + brick_154 = 154, + brick_157 = 157, + brick_158 = 158, + brick_168 = 168, + brick_176 = 176, + brick_178 = 178, + brick_179 = 179, + brick_180 = 180, + brick_190 = 190, + brick_191 = 191, + brick_192 = 192, + brick_193 = 193, + brick_194 = 194, + brick_195 = 195, + brick_196 = 196, + brick_198 = 198, + brick_199 = 199, + brick_200 = 200, + brick_208 = 208, + brick_209 = 209, + brick_210 = 210, + brick_211 = 211, + brick_212 = 212, + brick_213 = 213, + brick_216 = 216, + brick_217 = 217, + brick_218 = 218, + brick_219 = 219, + brick_220 = 220, + brick_221 = 221, + brick_222 = 222, + brick_223 = 223, + brick_224 = 224, + brick_225 = 225, + brick_226 = 226, + brick_232 = 232, + brick_268 = 268, + brick_301 = 301, + brick_302 = 302, + brick_303 = 303, + brick_304 = 304, + brick_305 = 305, + brick_306 = 306, + brick_307 = 307, + brick_308 = 308, + brick_309 = 309, + brick_310 = 310, + brick_311 = 311, + brick_312 = 312, + brick_313 = 313, + brick_314 = 314, + brick_315 = 315, + brick_316 = 316, + brick_317 = 317, + brick_318 = 318, + brick_319 = 319, + brick_320 = 320, + brick_321 = 321, + brick_322 = 322, + brick_323 = 323, + brick_324 = 324, + brick_325 = 325, + //brick_326 = 326, + brick_327 = 327, + brick_328 = 328, + brick_329 = 329, + brick_330 = 330, + brick_331 = 331, + brick_332 = 332, + brick_333 = 333, + brick_334 = 334, + brick_335 = 335, + brick_336 = 336, + brick_337 = 337, + brick_338 = 338, + brick_339 = 339, + brick_340 = 340, + brick_341 = 341, + brick_342 = 342, + brick_343 = 343, + brick_344 = 344, + brick_345 = 345, + brick_346 = 346, + brick_347 = 347, + brick_348 = 348, + brick_349 = 349, + brick_350 = 350, + brick_351 = 351, + brick_352 = 352, + brick_353 = 353, + brick_354 = 354, + brick_355 = 355, + brick_356 = 356, + brick_357 = 357, + brick_358 = 358, + brick_359 = 359, + brick_360 = 360, + brick_361 = 361, + brick_362 = 362, + brick_363 = 363, + brick_364 = 364, + brick_365 = 365, + roblox_1001 = 1001, + roblox_1002 = 1002, + roblox_1003 = 1003, + roblox_1004 = 1004, + roblox_1005 = 1005, + roblox_1006 = 1006, + roblox_1007 = 1007, + roblox_1008 = 1008, + roblox_1009 = 1009, + roblox_1010 = 1010, + roblox_1011 = 1011, + roblox_1012 = 1012, + roblox_1013 = 1013, + roblox_1014 = 1014, + roblox_1015 = 1015, + roblox_1016 = 1016, + roblox_1017 = 1017, + roblox_1018 = 1018, + roblox_1019 = 1019, + roblox_1020 = 1020, + roblox_1021 = 1021, + roblox_1022 = 1022, + roblox_1023 = 1023, + roblox_1024 = 1024, + roblox_1025 = 1025, + roblox_1026 = 1026, + roblox_1027 = 1027, + roblox_1028 = 1028, + roblox_1029 = 1029, + roblox_1030 = 1030, + roblox_1031 = 1031, + roblox_1032 = 1032 + }; + Number number; + typedef std::vector< BrickColor > Colors; + static const Colors& colorPalette(); // colors shown in UI + static const Colors& renderingPalette(); // colors supported by renderer + static const Colors& allColors(); // all known colors + + // returns the 0-based index of the color + size_t getClosestRenderingPaletteIndex() const; // closest "supported" palette index by the GFX engine. + size_t getClosestPaletteIndex() const; // closest palette index from the _whole_ palette list. + static const size_t paletteSize = 128; // supported by UI and data model. + static const size_t paletteSizeMSB = 7; // == log2(paletteSize) + + static void setRenderingSupportedPaletteSize(size_t maxSupportedColors); + + // Constructor/Factory + BrickColor(Number number):number(number) { + } + BrickColor():number(brick_194) { + } + explicit BrickColor(int number); + static BrickColor closest(G3D::Color3uint8 color); + static BrickColor closest(G3D::Color4uint8 color); + static BrickColor closest(G3D::Color3 color); + static BrickColor closest(G3D::Color4 color); + static BrickColor parse(const char* name); + static BrickColor random(); + inline static BrickColor brickWhite() + { + return brick_1; + } + inline static BrickColor brickGray() + { + return brick_194; + } + inline static BrickColor brickDarkGray() + { + return brick_199; + } + inline static BrickColor brickBlack() + { + return brick_26; + } + inline static BrickColor brickRed() + { + return brick_21; + } + inline static BrickColor brickYellow() + { + return brick_24; + } + inline static BrickColor brickGreen() + { + return brick_28; + } + inline static BrickColor baseplateGreen() + { + return brick_37; + } + inline static BrickColor brickBlue() + { + return brick_23; + } + inline static BrickColor defaultColor() + { + return BrickColor(); + } + + // Assignment + BrickColor& operator=(const BrickColor& other) + { + number = other.number; + return *this; + } + + //Query + G3D::Color4uint8 color4uint8() const; + G3D::Color3uint8 color3uint8() const; + G3D::Color4 color4() const; + G3D::Color3 color3() const; + const std::string& name() const; + + // returns the number as an int, not an ARGB + int asInt() const { return number; } + + // Comparison + bool operator==(const BrickColor& other) const { + return number==other.number; + } + bool operator!=(const BrickColor& other) const { + return number!=other.number; + } + bool operator>(const BrickColor& other) const { + return number>other.number; + } + bool operator<(const BrickColor& other) const { + return number + +namespace RBX { + + class Primitive; + class Camera; + + class RBXBaseClass CameraSubject : public virtual IHasLocation + { + public: + CameraSubject() {} + + virtual ~CameraSubject() {} + + // Old Camera Subject Stuff + /*implement*/ virtual void onCameraHeartbeat(const Vector3& cameraLocation, const Vector3& focusPoint) {} + /*implement*/ virtual const CoordinateFrame getRenderLocation() = 0; // goes to the rendering location, not the regular location + /*implement*/ virtual const Vector3 getRenderSize() = 0; + /*implement*/ virtual void onCameraNear(float distance) {} + /*implement*/ virtual void getCameraIgnorePrimitives(std::vector& primitives) {} + /*implement*/ virtual void getSelectionIgnorePrimitives(std::vector& primitives) {} + /*implement*/ virtual void stepRotationalVelocity(Vector3& cameraLocation, Vector3& focusLocation) {} + + protected: + class ContactManager* getContactManager(); + }; +} // namespace diff --git a/App/util/CellID.h b/App/util/CellID.h new file mode 100644 index 0000000..8913330 --- /dev/null +++ b/App/util/CellID.h @@ -0,0 +1,48 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ +#pragma once + +#include "G3D/Vector3.h" +#include "V8Tree/Instance.h" + +namespace RBX +{ + class CellID + { + private: + bool isNil; + RBX::Vector3 location; + shared_ptr terrainPart; + public: + CellID(); + CellID( bool isNil, const RBX::Vector3& location, shared_ptr terrainPart ) + :isNil(isNil) + ,location(location) + ,terrainPart(terrainPart) + { + } + CellID( bool newIsNil, float newLocation[3], shared_ptr newTerrainPart ); + ~CellID(); + + bool operator ==(const CellID& other) const + { + if (isNil != other.isNil) + return false; + if (location != other.location) + return false; + if (terrainPart != other.terrainPart) + return false; + return true; + } + + bool getIsNil() const { return isNil; } + void setIsNil( bool newIsNil ) { isNil = newIsNil; } + + G3D::Vector3 getLocation() const { return location; } + void setLocation( RBX::Vector3 newLocation ) { location = newLocation; } + + shared_ptr getTerrainPart() const { return terrainPart; } + void setTerrainPart( shared_ptr newTerrainPart ) { terrainPart = newTerrainPart; } + + static CellID fromParameters( bool newIsNil, float newLocation[3], shared_ptr newTerrainPart ) { return CellID( newIsNil, newLocation, newTerrainPart ); } + }; +} diff --git a/App/util/CheatEngine.h b/App/util/CheatEngine.h new file mode 100644 index 0000000..7a7be0c --- /dev/null +++ b/App/util/CheatEngine.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include "rbx/TaskScheduler.Job.h" +#include + +namespace RBX +{ + bool vmProtectedDetectCheatEngineIcon(); + + class HwndScanner + { + struct fullWindowInfo { + DWORD winWidth; + DWORD winHeight; + DWORD pid; + DWORD active; + std::string title; + bool kickEarly; + static size_t compareByPid ( fullWindowInfo lhs, fullWindowInfo rhs); + }; + std::vector hwndScanResults; + static BOOL CALLBACK makeHwndVector(HWND hwnd, LPARAM lParam); + + public: + HwndScanner(); + + int scan(); + + bool detectTitle() const; + + // This method will always return false on Windows8. + bool detectFakeAttach() const; + bool detectEarlyKick() const; + + }; + + class FileScanner + { + std::time_t baseTime; + std::string tempFolder; + public: + FileScanner(); + + bool detectLogUpdate() const; + }; + + extern bool ceDetected; + extern bool ceHwndChecks; + static const unsigned int kCeStructKey = 0x23A7F; + static const unsigned char kCeCharKey = 0x55; + + HANDLE setupCeLogWatcher(); + + // A job to profile this detection method and optionally enable it. + class VerifyConnectionJob : public RBX::TaskScheduler::Job + { + public: + VerifyConnectionJob(); + /*override*/ RBX::Time::Interval sleepTime(const Stats& stats); + /*override*/ Job::Error error(const Stats& stats); + /*override*/ TaskScheduler::StepResult step(const Stats& stats); + /*override*/ double getPriorityFactor(); + + }; + + bool isSandboxie(); + bool isCeBadDll(); + + // This class does dbvm detection and also breaks on certain dll injection methods. + class DbvmCanary + { + private: + HANDLE canaryCage; + HANDLE canaryHandle; + CONTEXT ctx; + size_t hashValue; + static void canary(HANDLE* mutex); + inline size_t hashDbgRegs(CONTEXT& ctx) + { + return (ctx.Dr0*54321) + (ctx.Dr1*98765) ^ (ctx.Dr2*6543) - (ctx.Dr3*987); + } + public: + DbvmCanary(); + void checkAndLocalUpdate(); + void kernelUpdate(); + }; + + class SpeedhackDetect + { + private: + DWORD k32base; + DWORD k32size; + public: + SpeedhackDetect(); + bool isSpeedhack(); + }; + + extern HeapValue vehHookLocationHv; + extern HeapValue vehStubLocationHv; + extern void* vehHookContinue; + + void addWriteBreakpoint(uintptr_t addr); + void removeWriteBreakpoint(uintptr_t addr); + + __declspec(align(4096)) extern int writecopyTrap[4096]; + +} // namespace RBX \ No newline at end of file diff --git a/App/util/ClusterCellIterator.h b/App/util/ClusterCellIterator.h new file mode 100644 index 0000000..28ec551 --- /dev/null +++ b/App/util/ClusterCellIterator.h @@ -0,0 +1,136 @@ +#pragma once + +#include "G3DCore.h" +#include "rbx/Debug.h" +#include "Voxel/Cell.h" +#include "Voxel/Util.h" +#include "Util/StreamRegion.h" +#include "Util/SpatialRegion.h" + +namespace RBX { + +class ClusterChunksIterator +{ +public: + ClusterChunksIterator() + : indexOfNextCellToIssue(0) + , internalSize(0) + { + } + + explicit ClusterChunksIterator(const std::vector& chunks) + : chunks(chunks) + , indexOfNextCellToIssue(0) + , internalSize(chunks.size() * kChunkSize) + { + } + + // single chunk + explicit ClusterChunksIterator(const SpatialRegion::Id& chunk) + : indexOfNextCellToIssue(0) + , internalSize(kChunkSize) + { + chunks.push_back(chunk); + } + + static inline void nextCellInIterationOrder(const Vector3int16& cellpos, Vector3int16* out) + { + unsigned int index = (cellpos.x & 0x1f) | ((cellpos.z & 0x1f) << 5) | ((cellpos.y & 0x0f) << 10); + + if (index == kChunkSize - 1) + { + // last cell in a chunk, it does not matter what we return except that it has to be a different chunk + *out = cellpos + Vector3int16(1, 0, 0); + } + else + { + unsigned int next = index + 1; + Vector3int16 local = Vector3int16((next & 0x1f), ((next >> 10) & 0xf), ((next >> 5) & 0x1f)); + + *out = SpatialRegion::globalVoxelCoordinateFromRegionAndRelativeCoordinate(SpatialRegion::regionContainingVoxel(cellpos), local); + } + } + + inline void pop(Vector3int16* out) + { + RBXASSERT(internalSize > 0); + + Vector3int16 local = Vector3int16((indexOfNextCellToIssue & 0x1f), ((indexOfNextCellToIssue >> 10) & 0xf), ((indexOfNextCellToIssue >> 5) & 0x1f)); + + *out = SpatialRegion::globalVoxelCoordinateFromRegionAndRelativeCoordinate(chunks[indexOfNextCellToIssue / kChunkSize], local); + + indexOfNextCellToIssue++; + internalSize--; + } + + inline bool chk(const Vector3int16& pos) const + { + return internalSize > 0; + } + + size_t size() const + { + return internalSize; + } + +private: + std::vector chunks; + size_t indexOfNextCellToIssue; + size_t internalSize; + + enum { kChunkSize = Voxel::kXZ_CHUNK_SIZE * Voxel::kXZ_CHUNK_SIZE * Voxel::kY_CHUNK_SIZE }; +}; + +// Cell iterator for 1/4 of a chunk. +struct OneQuarterClusterChunkCellIterator +{ + Vector3int16 cellOffset; + unsigned short internalCell; + unsigned short internalSize; + StreamRegion::Id regionId; + + OneQuarterClusterChunkCellIterator() + { + setToStartOfStreamRegion(StreamRegion::Id(0,0,0)); + } + + void setToStartOfStreamRegion(const StreamRegion::Id &_regionId) + { + regionId = _regionId; + internalSize = StreamRegion::getTotalVoxelVolumeOfARegion(); + internalCell = 0; + cellOffset = StreamRegion::getMinVoxelCoordinateInsideRegion(regionId); + } + + static inline void cellFromIndex(const Vector3int16 &cellOffset, unsigned int index, Vector3int16* out) { + (*out) = cellOffset + Vector3int16( + (index & 0xf), + ((index >> 8) & 0xf), + ((index >> 4) & 0xf)); + } + static inline void nextCellInIterationOrder(const Vector3int16& cellpos, Vector3int16* out) + { + Vector3int16 offset = StreamRegion::getMinVoxelCoordinateInsideRegion(StreamRegion::regionContainingVoxel(cellpos)); + Vector3int16 delta = cellpos - offset; + int index = delta.x | (delta.y << 8) | (delta.z << 4); + //RBXASSERT(index+1 < (int)StreamRegion::getTotalVoxelVolumeOfARegion()); + cellFromIndex(offset, index+1, out); + } + + inline void pop(Vector3int16* out) { + RBXASSERT(internalSize); + cellFromIndex(cellOffset, internalCell, out); + ++internalCell; + --internalSize; + } + + inline bool chk(const Vector3int16 &cellPos) const + { + return (internalSize > 0) && (StreamRegion::regionContainingVoxel(cellPos) == regionId); + } + inline size_t size() const { + return internalSize; + } +}; + +} diff --git a/App/util/Color.h b/App/util/Color.h new file mode 100644 index 0000000..d127fec --- /dev/null +++ b/App/util/Color.h @@ -0,0 +1,70 @@ +#pragma once + +#include "Util/G3DCore.h" + +/* + see http://web.media.mit.edu/~wad/color/palette.html + + This is an optimal 16 color palette + +Black RGB: 0, 0, 0 +Dk. Gray RGB: 87, 87, 87 +Red RGB: 173, 35, 35 +Blue RGB: 42, 75, 215 +Green RGB: 29, 105, 20 +Brown RGB: 129, 74, 25 +Purple RGB: 129, 38, 192 +Lt. Gray RGB: 160, 160, 160 +Lt. Green RGB: 129, 197, 122 +Lt. Blue RGB: 157, 175, 255 +Cyan RGB: 41, 208, 208 +Orange RGB: 255, 146, 51 +Yellow RGB: 255, 238, 51 +Tan RGB: 233, 222, 187 +Pink RGB: 255, 205, 243 +White RGB: 255, 255, 255 + +*/ + + +namespace RBX { + + class Color { + private: + G3D::Color3 rgb; + Color() {} + Color(unsigned char r, unsigned char g, unsigned char b) : rgb(static_cast(r)/255.0f, static_cast(g)/255.0f, static_cast(b)/255.0f) {} + const G3D::Color3& color3() {return rgb;} + public: + static const G3D::Color3& getColorByIndex(int i); + + inline static const G3D::Color3& black() {return getColorByIndex(0);} + inline static const G3D::Color3& darkGray() {return getColorByIndex(1);} + inline static const G3D::Color3& red() {return getColorByIndex(2);} + inline static const G3D::Color3& blue() {return getColorByIndex(3);} + inline static const G3D::Color3& green() {return getColorByIndex(4);} + inline static const G3D::Color3& brown() {return getColorByIndex(5);} + inline static const G3D::Color3& purple() {return getColorByIndex(6);} + inline static const G3D::Color3& lightGray() {return getColorByIndex(7);} + inline static const G3D::Color3& lightGreen() {return getColorByIndex(8);} + inline static const G3D::Color3& lightBlue() {return getColorByIndex(9);} + inline static const G3D::Color3& cyan() {return getColorByIndex(10);} + inline static const G3D::Color3& orange() {return getColorByIndex(11);} + inline static const G3D::Color3& yellow() {return getColorByIndex(12);} + inline static const G3D::Color3& tan() {return getColorByIndex(13);} + inline static const G3D::Color3& pink() {return getColorByIndex(14);} + inline static const G3D::Color3& white() {return getColorByIndex(15);} + + static const G3D::Color3& colorFromIndex8(int index); + + static const G3D::Color3 colorFromInt(unsigned int i); + + static const G3D::Color3 colorFromString(const std::string& s); + + static const G3D::Color3 colorFromPointer(void* pointer); + + static const G3D::Color3 colorFromTemperature(float temperature); // 0.0 = cold, 1.0 = hot + + static const G3D::Color3 colorFromError(double value); // 0.0 == not important, 10.0 == very important; + }; +} diff --git a/App/util/CompactEnum.h b/App/util/CompactEnum.h new file mode 100644 index 0000000..ccc14ea --- /dev/null +++ b/App/util/CompactEnum.h @@ -0,0 +1,25 @@ +#pragma once + +namespace RBX { + +template class CompactEnum +{ +public: + CompactEnum() + { + } + + CompactEnum(Enum value): data(value) + { + } + + operator Enum() const + { + return static_cast(data); + } + +private: + Storage data; +}; + +} diff --git a/App/util/ComputeProp.h b/App/util/ComputeProp.h new file mode 100644 index 0000000..b569f11 --- /dev/null +++ b/App/util/ComputeProp.h @@ -0,0 +1,68 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +//#include "Util/SoundWorld.h" +#include + + +namespace RBX { + + // A property that computes and caches its value + template + class ComputeProp + { + private: + Type val; + bool dirty; + O* object; + typedef Type (O::*GetFunc)(); + GetFunc getFunc; + + public: + ComputeProp(O* object, GetFunc getFunc) : + dirty(true), + object(object), + getFunc(getFunc) + {} + inline Type getValue() + { + if (dirty) { + val = (object->*getFunc)(); + dirty = false; + } + return val; + } + inline Type getLastComputedValue() const + { + RBXASSERT(!dirty); + return val; + } + + inline operator Type() + { + return getValue(); + } + inline Type* getValuePointer() + { + getValue(); + return &val; + } + inline Type& getValueRef() + { + getValue(); + return val; + } + bool setDirty() + { + bool didSomething = !dirty; + dirty = true; + return didSomething; + } + bool getDirty() const + { + return dirty; + } + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/util/ConcurrencyValidator.h b/App/util/ConcurrencyValidator.h new file mode 100644 index 0000000..72fdb9c --- /dev/null +++ b/App/util/ConcurrencyValidator.h @@ -0,0 +1,134 @@ +#pragma once + +#include "rbx/Debug.h" +#include "rbx/atomic.h" + + +#ifdef __RBX_NOT_RELEASE + #define RBX_USE_CONCURRENCY_VALIDATOR(expr) (expr) +#else + #define RBX_USE_CONCURRENCY_VALIDATOR(expr) ((void)0) +#endif + + + +namespace RBX { + +class ConcurrencyValidator +{ +private: + rbx::atomic writing; + std::string writeLocation; + mutable rbx::atomic reading; + +public: + ConcurrencyValidator() : writing(0), reading(0) + { + } + + ~ConcurrencyValidator() { + RBXASSERT(writing == 0); + RBXASSERT(reading == 0); + } + +private: + friend class WriteValidator; + friend class ReadOnlyValidator; + + bool preRead() const { + ++reading; + + long wasWriting = writing; + if (wasWriting != 0) {RBXASSERT(false && "writing check failed in preRead");} + return true; + } + + bool postRead() const { + long wasWriting = writing; + if (wasWriting != 0) {RBXASSERT(false && "wasWriting check failed in postRead");} + if (writing != 0) {RBXASSERT(false && "writing check failed in postRead");} + + --reading; + return true; + } + + bool preWrite() { + long wasWriting = writing; + long wasReading = reading; + + if (wasReading != 0) {RBXASSERT(false && "wasReading check failed in preWrite");} + if (wasWriting != 0) {RBXASSERT(false && "wasWriting check failed in preWrite");} + if (writing != 0) {RBXASSERT(false && "writing check failed in preWrite");} + + long result = ++writing; + + if (result != 1) {RBXASSERT(false && "InterlocedIncrement returned not -1 in preWrite");} + return true; + } + + bool preWrite(const std::string& writeWhere) { + bool okWrite = preWrite(); + if (okWrite) { + writeLocation = writeWhere; + } + return okWrite; + } + + bool postWrite() { + long wasWriting = writing; + long wasReading = reading; + + if (wasWriting != 1) {RBXASSERT(false && "wasWriting check failed in postWrite");} + if (writing != 1) {RBXASSERT(false && "writing check failed in postWrite");} + if (wasReading != 0) {RBXASSERT(false && "wasReading check failed in postWrite");} + if (reading != 0) {RBXASSERT(false && "reading check failed in postWrite");} + + long result = --writing; + if (result != 0) {RBXASSERT(false && "InterlocedIncrement returned not 0 in postWrite");} + if (reading != 0) {RBXASSERT(false && "reading check failed in postWrite");} + return true; + } +}; + +class ReadOnlyValidator +{ +private: + const ConcurrencyValidator& concurrencyValidator; + +public: + ReadOnlyValidator(const ConcurrencyValidator& c) : concurrencyValidator(c) { + RBX_USE_CONCURRENCY_VALIDATOR(concurrencyValidator.preRead()); + } + + ~ReadOnlyValidator() { + RBX_USE_CONCURRENCY_VALIDATOR(concurrencyValidator.postRead()); + } +}; + + +class WriteValidator +{ +private: + ConcurrencyValidator& concurrencyValidator; + +public: + WriteValidator(ConcurrencyValidator& c) : concurrencyValidator(c) { + RBX_USE_CONCURRENCY_VALIDATOR(concurrencyValidator.preWrite()); + } + + WriteValidator(ConcurrencyValidator& c, const std::string& writeWhere) : concurrencyValidator(c) { + RBX_USE_CONCURRENCY_VALIDATOR(concurrencyValidator.preWrite(writeWhere)); + } + + WriteValidator(ConcurrencyValidator& c, const char* writeWhere) : concurrencyValidator(c) { + RBX_USE_CONCURRENCY_VALIDATOR(concurrencyValidator.preWrite(writeWhere)); + } + + ~WriteValidator() { + RBX_USE_CONCURRENCY_VALIDATOR(concurrencyValidator.postWrite()); + } +}; + + +} // namespace + diff --git a/App/util/ContentFilter.h b/App/util/ContentFilter.h new file mode 100644 index 0000000..c2122b1 --- /dev/null +++ b/App/util/ContentFilter.h @@ -0,0 +1,55 @@ +#pragma once +#include "V8Tree/Instance.h" +#include "V8Tree/Service.h" +namespace RBX { + + extern const char* const sContentFilter; + class ContentFilter + : public DescribedNonCreatable + , public Service + + { + public: + typedef enum { Waiting, Succeeded, Failed } FilterResult; + static const unsigned MAX_CONTENT_FILTER_SIZE; + private: + struct ResultEntry + { + bool result; + int usageCount; + ResultEntry(bool result=false) + :result(result),usageCount(0) + {} + }; + typedef std::map ResultsDictionary; + typedef std::set RequestSet; + + ResultsDictionary resultsDictionary; + RequestSet requestSet; + + std::string url; + unsigned maxOutstandingRequests; + unsigned maxTableSize; + + static void truncateString(std::string& text); + + //Returns false if it doesn't know yet, may truncate the string + bool isContentFilterReady(const std::string& value); + bool isStringSafe(std::string& value); + + void cleanTable(); + public: + ContentFilter(); + ~ContentFilter(); + + + FilterResult getStringState(std::string& value); + + void setFilterUrl(std::string); + void setFilterLimits(int,int); + + void doFilterRequest(std::string value); + void saveFilterResult(std::string value, bool result); + }; + +} diff --git a/App/util/ContentId.cpp b/App/util/ContentId.cpp index 21e78a0..1755b99 100644 --- a/App/util/ContentId.cpp +++ b/App/util/ContentId.cpp @@ -195,7 +195,7 @@ namespace RBX std::string host = parsed.host(); std::string path = parsed.path(); - static const std::string testsite_domain = "pizzaboxer.fun"; + static const std::string testsite_domain = "robloxlabs.com"; const RBX::Url baseUrlParsed = RBX::Url::fromString(baseUrl); @@ -278,7 +278,7 @@ namespace RBX if (boost::istarts_with(path, paths[i]) && (path.size() == pathLength || path[pathLength] == '?')) { - static const char* domain = ".pizzaboxer.fun"; + static const char* domain = ".robloxlabs.com"; if (DFFlag::UrlReconstructToAssetGame) { diff --git a/App/util/ContentId.h b/App/util/ContentId.h new file mode 100644 index 0000000..bd6d107 --- /dev/null +++ b/App/util/ContentId.h @@ -0,0 +1,80 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#ifndef _4B0F5828DADB441bA2D2FDCBCB5538A6 +#define _4B0F5828DADB441bA2D2FDCBCB5538A6 + +#include "stdio.h" +#include "util/name.h" +#include +#include +#include +#include +#include "rbx/boost.hpp" +#include +#include + +using boost::shared_ptr; + +namespace RBX { + + class ContentId + { + public: + static ContentId fromUrl(const std::string& url); + static ContentId fromAssets(const char* filePath); // filePath is a relative pathname within the "Content" directory + static ContentId fromGameAssetName(const std::string& gameAssetName); + + // Constructors are explicit to encourage you to use static constructors above if the string isn't a fully qualified URL + explicit ContentId(const char* id) + :id(id) { CorrectBackslash(this->id); } + explicit ContentId(const std::string& id) + :id(id) { CorrectBackslash(this->id); } + ContentId() {} + + void clear() + { + id.clear(); + } + + const char* c_str() const { + return id.c_str(); + } + const std::string& toString() const { + return id; + } + + void convertToLegacyContent(const std::string& baseUrl); + void convertAssetId(const std::string& baseUrl, int universeId); + bool reconstructUrl(const std::string& baseUrl, const char* const paths[], const int pathCount); + bool reconstructAssetUrl(const std::string& baseUrl); + + std::string getAssetId() const; + std::string getAssetName() const; + std::string getUnConvertedAssetName() const; + + bool isNull() const { return id.size()==0; } + bool isAsset() const { return id.compare(0, 11, "rbxasset://") == 0; } + bool isAssetId() const { return id.compare(0, 13, "rbxassetid://") == 0; } + bool isHttp() const { return id.compare(0, 4, "http") == 0; } + bool isFile() const { return id.compare(0, 7, "file://") == 0; } + bool isRbxHttp() const { return id.compare(0, 10, "rbxhttp://") == 0; } + bool isAppContent() const { return id.compare(0, 9, "rbxapp://") == 0; } + bool isNamedAsset() const; + bool isConvertedNamedAsset() const; + + friend bool operator<(const ContentId& a, const ContentId& b); + friend bool operator==(const ContentId& a, const ContentId& b); + friend bool operator!=(const ContentId& a, const ContentId& b); + + private: + static void CorrectBackslash(std::string& id); + + std::string id; + }; + + std::size_t hash_value(const ContentId& id); + +}// namespace + + +#endif diff --git a/App/util/ContentProviderJob.h b/App/util/ContentProviderJob.h new file mode 100644 index 0000000..5f9aff7 --- /dev/null +++ b/App/util/ContentProviderJob.h @@ -0,0 +1,51 @@ +#pragma once + +#include "Util/AsyncHttpQueue.h" +#include "v8datamodel/DataModelJob.h" +#include "rbx/threadsafe.h" + +namespace RBX +{ + class DataModel; + +class ContentProviderJob : public DataModelJob +{ +public: + enum ExecutionMode + { + JobMode, + ImmediateMode + }; + +private: + boost::function)> processFunc; + boost::function errorFunc; + + struct ContentProviderTask + { + std::string id; + shared_ptr data; + }; + bool aborted; + rbx::safe_queue tasks; + ExecutionMode execMode; + + TaskScheduler::StepResult processTask(const ContentProviderTask& task); +public: + + ContentProviderJob(shared_ptr dataModel, const char* name, + boost::function)> processFunc, + boost::function errorFunc); + + /*override*/ Time::Interval sleepTime(const Stats& stats); + /*override*/ Job::Error error(const Stats& stats); + /*override*/ TaskScheduler::StepResult stepDataModelJob(const Stats& stats); + + + void abort(); + void addTask(const std::string& id, AsyncHttpQueue::RequestResult result, std::istream* filestream, shared_ptr data); + void setExecutionMode(ExecutionMode execMode); +}; + + +} \ No newline at end of file diff --git a/App/util/ControlledLRUCache.h b/App/util/ControlledLRUCache.h new file mode 100644 index 0000000..9881adf --- /dev/null +++ b/App/util/ControlledLRUCache.h @@ -0,0 +1,215 @@ +#pragma once + +#include "Util/LRUCache.h" +namespace RBX +{ + enum CacheSizeEnforceMethod { CACHE_ENFORCE_MEMORY_SIZE, CACHE_ENFORCE_OBJECT_COUNT }; + + template + class ControlledLRUCache + { + private: + unsigned long maxSize; + CacheSizeEnforceMethod enforceMethod; + boost::scoped_ptr< LRUCache > evictableCache; // items that are processed and ok to delete from cache + boost::scoped_ptr< LRUCache > pinnedCache; // items that are pending processing/use +public: + public: + ControlledLRUCache( const unsigned long maxSize, CacheSizeEnforceMethod method = CACHE_ENFORCE_OBJECT_COUNT) : maxSize(maxSize), enforceMethod(method) + { + if (method == CACHE_ENFORCE_OBJECT_COUNT) + evictableCache.reset(new SizeEnforcedLRUCache(maxSize)); + else if (method == CACHE_ENFORCE_MEMORY_SIZE) + evictableCache.reset(new MemEnforcedLRUCache(maxSize)); + else + RBXASSERT(false); + + pinnedCache.reset(new LRUCache()); + } + + ~ControlledLRUCache() + {} + + inline const unsigned long size() + { + return evictableCache->size() + pinnedCache->size(); + } + + inline const unsigned long memSize() + { + return evictableCache->memSize() + pinnedCache->memSize(); + } + + void clear() + { + evictableCache.clear(); + pinnedCache.clear(); + } + + inline bool exists( const Key &key ) const + { + return (evictableCache->exists(key) || pinnedCache->exists(key)); + } + inline bool remove( const Key &key ) + { + bool result = false; + result = evictableCache->remove(key) || result; + result = pinnedCache->remove(key) || result; + return result; + } + + inline void markEvictable(const Key& key) + { + Data data; + unsigned long size; + if(pinnedCache->fetch(key, &data, &size)){ + internalMakeEvictable(key, data, size); + } + } + + inline bool fetch( const Key &key, Data* result, bool makeEvictable) + { + if(evictableCache->fetch(key, result)) + return true; + + unsigned long size; + + if(pinnedCache->fetch(key, result, &size)){ + if(!makeEvictable) + return true; + + //Make it evictable by moving it into the evictableCache + internalMakeEvictable(key, *result, size); + return true; + } + + //Didn't find it, return false + return false; + } + + inline void resize( unsigned long newSize) + { + maxSize = newSize; + evictableCache->resize(newSize); + pinnedCache->resize(newSize); + + unsigned long curSize = (enforceMethod == CACHE_ENFORCE_MEMORY_SIZE) ? memSize() : size(); + while((curSize > newSize) && (evictableCache->size() > 0)) + { + evictableCache->removeLeastRecentlyUsed(); + curSize = (enforceMethod == CACHE_ENFORCE_MEMORY_SIZE) ? memSize() : size(); + } + + RBXASSERT((enforceMethod == CACHE_ENFORCE_MEMORY_SIZE ? memSize() : size()) <= newSize); + } + + inline void insert( const Key &key, const Data &data, unsigned long dataSize = 0 ) + { + //First remove it from evictableCache, since it will go into pinnedCache now + evictableCache->remove(key); + pinnedCache->remove(key); + + unsigned int new_size = (enforceMethod == CACHE_ENFORCE_MEMORY_SIZE) ? (this->memSize() + dataSize) : (this->size() + 1); + if(new_size > maxSize) { + //We are full, see if we have evictable space + if(evictableCache->size() > 0){ + //Something is evictable, kick it out + evictableCache->removeLeastRecentlyUsed(); + } + } + + pinnedCache->insert(key, data, dataSize); + } + inline bool isFull() + { + return pinnedCache->size() >= maxSize; + } + + + inline bool evictAll() + { + if(!pinnedCache->empty()){ + evictableCache->insert(pinnedCache->begin(), pinnedCache->end()); + pinnedCache->clear(); + return true; + } + return false; + } + private: + void internalMakeEvictable(const Key& key, const Data& data, unsigned long dataSize) + { + evictableCache->insert(key,data, dataSize); + pinnedCache->remove(key); + + RBXASSERT(size() <= maxSize); + } + }; + + template + class ConcurrentControlledLRUCache + { + private: + RBX::ControlledLRUCache cache; + boost::mutex mutex; + + unsigned long resetCounter; + unsigned long heartbeatCounter; + public: + ConcurrentControlledLRUCache(unsigned long size, unsigned long resetCounter, CacheSizeEnforceMethod enforceMethod = CACHE_ENFORCE_OBJECT_COUNT) + :cache(size, enforceMethod) + ,resetCounter(resetCounter) + ,heartbeatCounter(0) + {} + + inline bool fetch( const Key &key, Data* result, bool makeEvictable) + { + boost::mutex::scoped_lock lock(mutex); + return cache.fetch(key, result, makeEvictable); + } + + inline void resize( unsigned long newSize) + { + boost::mutex::scoped_lock lock(mutex); + cache.resize(newSize); + } + + inline void insert( const Key &key, const Data &data, unsigned long dataSize = 0) + { + boost::mutex::scoped_lock lock(mutex); + return cache.insert(key, data, dataSize); + } + + inline bool remove( const Key &key ) + { + boost::mutex::scoped_lock lock(mutex); + return cache.remove(key); + } + + inline void markEvictable(const Key& key) + { + boost::mutex::scoped_lock lock(mutex); + cache.markEvictable(key); + } + + inline bool isFull() + { + boost::mutex::scoped_lock lock(mutex); + return cache.isFull(); + } + + inline bool evictAll() + { + boost::mutex::scoped_lock lock(mutex); + return cache.evictAll(); + } + + inline void onHeartbeat() + { + if(++heartbeatCounter >= resetCounter){ + heartbeatCounter = 0; + evictAll(); + } + } + }; +} + diff --git a/App/util/Cursors.h b/App/util/Cursors.h new file mode 100644 index 0000000..3f59c93 --- /dev/null +++ b/App/util/Cursors.h @@ -0,0 +1,2 @@ +#pragma once + diff --git a/App/util/Darwin/HttpCocoa.mm b/App/util/Darwin/HttpCocoa.mm index df3c47a..574b888 100644 --- a/App/util/Darwin/HttpCocoa.mm +++ b/App/util/Darwin/HttpCocoa.mm @@ -303,7 +303,7 @@ static NSString* kHttpRunLoopMode = @"RobloxHttpController"; if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_8) { if ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) - return [protectionSpace.host rangeOfString:@".pizzaboxer.fun"].location != NSNotFound; + return [protectionSpace.host rangeOfString:@".robloxlabs.com"].location != NSNotFound; } return NO; } @@ -313,7 +313,7 @@ static NSString* kHttpRunLoopMode = @"RobloxHttpController"; if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_8) { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] && - ([challenge.protectionSpace.host rangeOfString:@".pizzaboxer.fun"].location != NSNotFound)) + ([challenge.protectionSpace.host rangeOfString:@".robloxlabs.com"].location != NSNotFound)) { // trust the credentials... [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; @@ -353,7 +353,7 @@ int rbx_isRobloxSite(const char* url) if (!isRobloxUrl) { - textRange =[host rangeOfString:@".pizzaboxer.fun"]; + textRange =[host rangeOfString:@".robloxlabs.com"]; isRobloxUrl = textRange.location != NSNotFound; } diff --git a/App/util/DoubleEndedVector.h b/App/util/DoubleEndedVector.h new file mode 100644 index 0000000..1659aec --- /dev/null +++ b/App/util/DoubleEndedVector.h @@ -0,0 +1,84 @@ +#pragma once + +#include "rbx/Debug.h" + +#include + +namespace RBX { + +/** + * Wrapper around std::vector that allows for quick insert and pop from both + * front and back. This container never attempts to reduce the amount of + * memory it uses, so it may not be suitable for queues that do not have a + * reasonable upper bound in size. + */ +template +struct DoubleEndedVector { +private: + size_t head; + size_t internalSize; + std::vector data; + size_t dataSizeMask; + + void grow() { + if (internalSize == data.size()) { + std::vector replacement(std::max((size_t)32, internalSize * 2)); + + if (data.size() > 0) { + size_t firstSegment = data.size() - head; + size_t secondSegment = internalSize - firstSegment; + + RBXASSERT(firstSegment + secondSegment == data.size()); + std::copy(&data[head], &data[head] + firstSegment, &replacement[0]); + std::copy(&data[0], &data[0] + secondSegment, &replacement[firstSegment]); + } + + head = 0; + data.swap(replacement); + RBXASSERT((data.size() & (data.size() - 1)) == 0); + dataSizeMask = data.size() - 1; + } + } + +public: + DoubleEndedVector() : head(0), internalSize(0), data(), dataSizeMask(0) {} + + size_t size() const { + return internalSize; + } + + bool push_back(const T& inputData) { + grow(); + data[(head + internalSize) & dataSizeMask] = inputData; + internalSize++; + return true; + } + bool push_front(const T& inputData) { + grow(); + + size_t newHead = (head - 1) & dataSizeMask; + + data[newHead] = inputData; + head = newHead; + internalSize++; + return true; + } + + void pop_front(T* out) { + RBXASSERT(internalSize > 0); + (*out) = data[head]; + head = (head + 1) & dataSizeMask; + internalSize--; + } + + inline T& operator[](const unsigned int& idx) { + return data[(head + idx) & dataSizeMask]; + } + inline const T& operator[](const unsigned int& idx) const { + return data[(head + idx) & dataSizeMask]; + } +}; + + + +} \ No newline at end of file diff --git a/App/util/Exception.h b/App/util/Exception.h new file mode 100644 index 0000000..edab86d --- /dev/null +++ b/App/util/Exception.h @@ -0,0 +1,8 @@ + +#pragma once + +#include + +namespace RBX +{ +} diff --git a/App/util/ExponentialRunningAverage.h b/App/util/ExponentialRunningAverage.h new file mode 100644 index 0000000..13cccae --- /dev/null +++ b/App/util/ExponentialRunningAverage.h @@ -0,0 +1,68 @@ +#pragma once + +#include "Util/G3DCore.h" + +namespace RBX { + + // ToDo - templatize this + class floatERA { + private: + float weight; + float avg; + public: + floatERA() + : weight(.5f) + { + reset(); + } + + floatERA(float weight) + : weight(weight) + { + reset(); + } + + void reset() { + avg = 0.0f; + } + + float pushAndGetAverage(float value) { + avg = (weight * (value - avg)) + avg; + return avg; + } + + float getAverage() {return avg;} + }; + + + + class Vector3ERA { + private: + float weight; + Vector3 avg; + public: + Vector3ERA() + :weight(.5f) + {} // Vector inits to zeros + + Vector3ERA(float weight) + : weight(weight) + {} // Vector3 inits to zeros; + + void reset(const Vector3& value) { + avg = value; + } + + void reset() { + reset(Vector3::zero()); + } + + void push(const Vector3& value) { + avg += weight * (value - avg); + } + + const Vector3& getAverage() {return avg;} + }; +} + + diff --git a/App/util/Extents.h b/App/util/Extents.h new file mode 100644 index 0000000..eddf985 --- /dev/null +++ b/App/util/Extents.h @@ -0,0 +1,239 @@ +#pragma once + +#include "Util/NormalId.h" +#include "Util/G3DCore.h" +#include "rbx/Debug.h" +#include "Util/Math.h" + +namespace RBX { + + class Extents { + private: + Vector3 low; + Vector3 high; + + public: + Extents() // initialize to negativeInfiniteExtents; + : low(Vector3::maxFinite()) + , high(-Vector3::maxFinite()) + {} + + Extents(const Vector3& _min, const Vector3& _max) + : low(_min) + , high(_max) + { + RBXASSERT_SLOW(low == low.min(high)); + RBXASSERT_SLOW(high == high.max(low)); + } + + bool isNanInf() const + { + return (Math::isNanInfVector3(low) || Math::isNanInfVector3(high)); + } + + static Extents fromCenterCorner(const Vector3& center, const Vector3& corner) { + return Extents(center-corner, center+corner); + } + + static Extents fromCenterRadius(const Vector3& center, float radius) { + return fromCenterCorner(center, Vector3(radius, radius, radius)); + } + + bool operator==(const Extents& other) const { + return ((low == other.low) && (high == other.high)); + } + + bool operator!=(const Extents& other) const { + return !(*this == other); + } + + static Extents vv(const Vector3& v0, const Vector3& v1) { + Extents e; + e.low = v0.min(v1); + e.high = v0.max(v1); + return e; + } + + const Vector3& min() const {return low;} + const Vector3& max() const {return high;} + + Vector3int16 getCornerIndex(int i) const; + Vector3 getCorner(int i) const; + + Vector3 size() const { + return high - low; + } + + Vector3 center() const { + return 0.5 * (low + high); + } + + Vector3 bottomCenter() const { + Vector3 answer = center(); + answer.y = low.y; + return answer; + } + + Vector3 topCenter() const { + Vector3 answer = center(); + answer.y = high.y; + return answer; + } + + float longestSide() const { + Vector3 s = size(); + return G3D::max(G3D::max(s.x, s.y), s.z); + } + + float volume() const { + Vector3 s = size(); + return s.x * s.y * s.z; + } + + float areaXZ() const { + Vector3 s = size(); + return s.x * s.z; + } + + bool isNull() const { + return low.x > high.x || low.y > high.y || low.z > high.z; + } + + Extents toWorldSpace(const CoordinateFrame& offset) const; + + Extents express(const CoordinateFrame& myFrame, const CoordinateFrame& expressInFrame) const; + + // faceId's are in order of x, y, z, -x, -y, -z + Vector3 faceCenter(NormalId faceId) const; + + /** + Returns the four corners of a face (0 <= f < 6). + The corners are returned to form a counter clockwise quad facing outwards. + */ + void getFaceCorners( + NormalId faceId, + Vector3& v0, + Vector3& v1, + Vector3& v2, + Vector3& v3) const; + + Plane getPlane(NormalId normalId) const; + + // clip the vector to be inside the Extents + Vector3 clip(const Vector3& clipVector) const { + return clipVector.clamp(low, high); + } + + + float computeClosestSqDistanceToPoint(const Vector3& point) const; + + // minimum amount to move innerExtents and keep them within Extents + Vector3 clamp(const Extents& innerExtents) const; + + NormalId closestFace(const Vector3& point); + + void unionWith(const Extents& other) { // Extents& operator&= (const Extents& other) { + low = low.min(other.low); + high = high.max(other.high); + } + + Extents clampInsideOf(const Extents& other) const; // Extents& operator&= (const Extents& other) { + + void shift(const Vector3& shiftVector) {// Extents& operator+= (const Vector3& shiftVector) { + low += shiftVector; + high += shiftVector; + } + + void scale(float x) { // Extents& operator*= (float x) { + low *= x; + high *= x; + } + + void expand(float x) { + low -= Vector3(x,x,x); + high += Vector3(x,x,x); + } + + void expand(const Vector3& p) { + low -= p; + high += p; + } + + void expandToContain(const Vector3& p) { + low = low.min(p); + high = high.max(p); + } + + void expandToContain(const Extents& e) { + low = low.min(e.low); + high = high.max(e.high); + } + + bool contains(const Vector3& point) const { + return ( (point.x >= low.x) + && (point.y >= low.y) + && (point.z >= low.z) + && (point.x <= high.x) + && (point.y <= high.y) + && (point.z <= high.z) ); + } + + bool fuzzyContains(const Vector3& point, float slop) const { + return ( (point.x >= (low.x - slop)) + && (point.y >= (low.y - slop)) + && (point.z >= (low.z - slop)) + && (point.x <= (high.x + slop)) + && (point.y <= (high.y + slop)) + && (point.z <= (high.z + slop)) ); + } + + + bool overlapsOrTouches(const Extents& other) const { // true if sides are exactly equal (touching) + return ( (this->low.x > other.high.x) + || (this->low.y > other.high.y) + || (this->low.z > other.high.z) + || (this->high.y < other.low.y) + || (this->high.x < other.low.x) + || (this->high.z < other.low.z)) ? false : true; + } + + static bool overlapsOrTouches(const Extents& e0, const Extents& e1) {return e0.overlapsOrTouches(e1);} + + bool clampToOverlap(const Extents& other) { + if ( (this->low.x >= other.high.x) + || (this->low.y >= other.high.y) + || (this->low.z >= other.high.z) + || (this->high.y <= other.low.y) + || (this->high.x <= other.low.x) + || (this->high.z <= other.low.z)) + return false; // not overlap + low = low.clamp(other.low, other.high); + high = high.clamp(other.low, other.high); + return true; + } + + bool separatedByMoreThan(const Extents& other, float distance) const; + + + static const Extents& zero() { + static Extents e(Vector3::zero(), Vector3::zero()); + return e; + } + + static const Extents& unit() { + static Extents e(Vector3(-1,-1,-1), Vector3(1,1,1)); + return e; + } + + static const Extents& negativeMaxExtents() { + static Extents e; // default constructor builds this; + return e; + } + + static const Extents& maxExtents() { + static Extents e(-Vector3::maxFinite(), Vector3::maxFinite()); + return e; + } + + }; +} // namespace diff --git a/App/util/ExtentsInt32.h b/App/util/ExtentsInt32.h new file mode 100644 index 0000000..f37170e --- /dev/null +++ b/App/util/ExtentsInt32.h @@ -0,0 +1,173 @@ +#pragma once + +#include "Util/Vector3int32.h" +#include "Util/Extents.h" +#include "rbx/Debug.h" + +namespace RBX { + + class ExtentsInt32 { + public: + Vector3int32 low; + Vector3int32 high; + + ExtentsInt32() + : low(Vector3int32::maxInt()) + , high(Vector3int32::minInt()) + {} + + ExtentsInt32(const Vector3int32& _min, const Vector3int32& _max) + : low(_min) + , high(_max) + { + RBXASSERT_SLOW(low == low.min(high)); + RBXASSERT_SLOW(high == high.max(low)); + } + + bool operator==(const ExtentsInt32& other) const { + return ((low == other.low) && (high == other.high)); + } + + bool operator!=(const ExtentsInt32& other) const { + return !(*this == other); + } + + ExtentsInt32& operator= (const ExtentsInt32& other) { + low = other.low; + high = other.high; + return *this; + } + + ExtentsInt32 shiftRight(int shift) const { + RBXASSERT_SLOW(shift >= 0); + RBXASSERT_SLOW(shift <= 32); + return ExtentsInt32(low >> shift, high >> shift); + } + + ExtentsInt32 shiftRight(const Vector3int32& shift) const { + return ExtentsInt32(low >> shift, high >> shift); + } + + ExtentsInt32 shiftLeft(int shift) const { + RBXASSERT_SLOW(shift >= 0); + RBXASSERT_SLOW(shift <= 32); + return ExtentsInt32(low << shift, high << shift); + } + + ExtentsInt32 shiftLeft(const Vector3int32& shift) const { + return ExtentsInt32(low << shift, high << shift); + } + + static ExtentsInt32 vv(const Vector3int32& v0, const Vector3int32& v1) { + ExtentsInt32 e; + e.low = v0.min(v1); + e.high = v0.max(v1); + return e; + } + + const Vector3int32& min() const {return low;} + const Vector3int32& max() const {return high;} + + Vector3int32 getCorner(int i) const; + + Vector3int32 size() const { + return high - low; + } + + Vector3int32 center() const { + return ((low + high) >> 1); + } + + Vector3int32 bottomCenter() const { + Vector3int32 answer = center(); + answer.y = low.y; + return answer; + } + + Vector3int32 topCenter() const { + Vector3int32 answer = center(); + answer.y = high.y; + return answer; + } + + int longestSide() const { + Vector3int32 s = size(); + return std::max(std::max(s.x, s.y), s.z); + } + + int volume() const { + Vector3int32 s = size(); + long long int answer = s.x * s.y * s.z; + RBXASSERT(answer < INT_MAX); + return static_cast(answer); + } + + static ExtentsInt32 unionExtents(const ExtentsInt32& a, const ExtentsInt32& b) { + return ExtentsInt32(a.low.min(b.low), a.high.max(b.high)); + } + + void shift(const Vector3int32& shiftVector) { + low = low + shiftVector; + high = high + shiftVector; + } + + void expand(int x) { + low = low - Vector3int32(x,x,x); + high = high + Vector3int32(x,x,x); + } + + const Vector3int32& operator[] (int i) const { + return ((Vector3int32*)this)[i]; + } + + Vector3int32& operator[] (int i) { + return ((Vector3int32*)this)[i]; + } + + operator Vector3int32* () { + return (Vector3int32*)this; + } + operator const Vector3int32* () const { + return (Vector3int32*)this; + } + + bool contains(int x, int y, int z) const { + return ( (x >= low.x) + && (y >= low.y) + && (z >= low.z) + && (x <= high.x) + && (y <= high.y) + && (z <= high.z) ); + } + + bool contains(const Vector3int32& point) const { + return contains(point.x, point.y, point.z); + } + + bool overlapsOrTouches(const ExtentsInt32& other) const { // true if sides are exactly equal (touching) + return ( (this->low.x > other.high.x) + || (this->low.y > other.high.y) + || (this->low.z > other.high.z) + || (this->high.y < other.low.y) + || (this->high.x < other.low.x) + || (this->high.z < other.low.z)) ? false : true; + } + + static bool overlapsOrTouches(const ExtentsInt32& e0, const ExtentsInt32& e1) {return e0.overlapsOrTouches(e1);} + + Extents toExtents() const { + return Extents(low.toVector3(), high.toVector3()); + } + + static const ExtentsInt32& zero() { + static ExtentsInt32 e(Vector3int32::zero(), Vector3int32::zero()); + return e; + } + + static const ExtentsInt32& empty() { + static ExtentsInt32 e; // constructor sets negatives + return e; + } + + }; +} // namespace diff --git a/App/util/Face.h b/App/util/Face.h new file mode 100644 index 0000000..31eb250 --- /dev/null +++ b/App/util/Face.h @@ -0,0 +1,76 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "Util/NormalId.h" +#include "rbx/Debug.h" + +namespace RBX { + + class Extents; + + class Face + { + private: + Vector3 c0, c1, c2, c3; + + Face(const Vector3& c0, const Vector3& c1, const Vector3& c2, const Vector3& c3) + : c0(c0), c1(c1), c2(c2), c3(c3) + {} + + Vector3 getAxis(int i) const { + RBXASSERT((i == 0) || (i==1)); + return i == 0 ? getU() : getV(); + } + + void minMax(const Vector3& point, const Vector3& normal, float& min, float& max) const; + + Face operator* (float fScalar) const { + return Face(fScalar*c0, fScalar*c1, fScalar*c2, fScalar*c3); + } + + Face operator* (const Vector3& vector3) const { + return Face(vector3*c0, vector3*c1, vector3*c2, vector3*c3); + } + + public: + Face() {} + + Face(const Face& other) + : c0(other.c0), c1(other.c1), c2(other.c2), c3(other.c3) + {} + + static Face fromExtentsSide(const Extents& e, NormalId faceId); + + void snapToGrid(float grid); + + Vector3& operator[] (int i); + + const Vector3& operator[] (int i) const; + + Vector3 getU() const {return (c1 - c0).direction();} + + Vector3 getV() const {return (c3 - c0).direction();} + + Vector3 getNormal() const {return getU().cross(getV()).direction();} + + Vector2 size() const {return Vector2((c1-c0).magnitude(), (c3-c0).magnitude());} + + Vector3 center() const {return 0.5 * (c0 + c2);} + + // Create new faces + Face toWorldSpace(const CoordinateFrame& objectCoord) const; + + Face toObjectSpace(const CoordinateFrame& objectCoord) const; + + Face projectOverlapOnMe(const Face& other) const; + + // Tests + bool fuzzyContainsInExtrusion(const Vector3& point, float tolerance) const; + + static bool cornersAligned(const Face& f0, const Face& f1, float tolerance); + + static bool hasOverlap(const Face& f0, const Face& f1, float byAtLeast); + + static bool overlapWithinPlanes(const Face& f0, const Face& f1, float tolerance); + }; +} // namespace RBX diff --git a/App/util/Faces.h b/App/util/Faces.h new file mode 100644 index 0000000..02c6dfc --- /dev/null +++ b/App/util/Faces.h @@ -0,0 +1,29 @@ +/* Copyright 2003-2009 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/NormalId.h" + +namespace RBX { + + //A utility class for holding a set of "Faces" associated with an object (top, bottom, left, right, front, back) + class Faces + { + public: + Faces(int normalIdMask = 0); + void clear() { normalIdMask = NORM_NONE_MASK; } + void setNormalId(NormalId normalId, bool value); + bool getNormalId(NormalId normalId) const; + + + bool operator==(const Faces& other) const { + return normalIdMask == other.normalIdMask; + } + bool operator!=(const Faces& other) const { + return normalIdMask != other.normalIdMask; + } + + + int normalIdMask; + }; +} diff --git a/App/util/FileSystem.h b/App/util/FileSystem.h new file mode 100644 index 0000000..fb18518 --- /dev/null +++ b/App/util/FileSystem.h @@ -0,0 +1,32 @@ +#pragma once + +#include "FastLog.h" + +#include +#include +#include +#include + +DYNAMIC_LOGGROUP(FileSystem) + +namespace RBX +{ + +enum FileSystemDir +{ + DirAppData = 0, + DirPicture, + DirVideo, + DirExe +}; + +namespace FileSystem +{ + boost::filesystem::path getUserDirectory(bool create, FileSystemDir dir, const char *subDirectory = 0); + boost::filesystem::path getCacheDirectory(bool create, const char* subDirectory); + boost::filesystem::path getTempFilePath(); + boost::filesystem::path getLogsDirectory(); + + void clearCacheDirectory(const char* subDirectory); +}; +} // namespace RBX diff --git a/App/util/FileSystemIndependent.cpp b/App/util/FileSystemIndependent.cpp index 84284cf..d115f9a 100644 --- a/App/util/FileSystemIndependent.cpp +++ b/App/util/FileSystemIndependent.cpp @@ -81,7 +81,7 @@ boost::filesystem::path getBaseCacheDirectory(bool create) boost::filesystem::path path = boost::filesystem::temp_directory_path(); #ifndef RBX_PLATFORM_IOS - path /= "watrbx"; + path /= "Roblox"; #endif #if defined(_DEBUG) || defined(_NOOPT) diff --git a/App/util/FixedArray.h b/App/util/FixedArray.h new file mode 100644 index 0000000..2243bce --- /dev/null +++ b/App/util/FixedArray.h @@ -0,0 +1,71 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "boost/array.hpp" +#include "rbx/Debug.h" + +namespace RBX { + +/* USAGE + +*/ + template + class FixedArray + { + private: + boost::array data; + size_t num; + + public: + FixedArray() + : num(0) + {} + + void push_back(const T& x) { + RBXASSERT_VERY_FAST(num < N); + data[num] = x; + ++num; + } + + void fastRemove(size_t i) { + RBXASSERT_VERY_FAST(i < num); + RBXASSERT_VERY_FAST(num <= N); + data[i] = data[num - 1]; + --num; + } + + void replace(size_t i, const T& x) { + RBXASSERT_VERY_FAST(i < num); + RBXASSERT_VERY_FAST(num <= N); + data[i] = x; + } + + void fastClear() { + num = 0; + } + + T operator[](size_t i) { + RBXASSERT_VERY_FAST(i < num); + return data[i]; + } + + const T operator[](size_t i) const { + RBXASSERT_VERY_FAST(i < num); + return data[i]; + } + + size_t size() const { + return num; + } + + size_t capacity() const + { + return N; + } + }; + +}// namespace + + + diff --git a/App/util/FixedSizeCircularBuffer.h b/App/util/FixedSizeCircularBuffer.h new file mode 100644 index 0000000..c031900 --- /dev/null +++ b/App/util/FixedSizeCircularBuffer.h @@ -0,0 +1,36 @@ +#pragma once + +namespace RBX { + +template +struct FixedSizeCircularBuffer { +private: + ElementType data[size]; + unsigned int head; + unsigned int pushed; +public: + + FixedSizeCircularBuffer() : head(0), pushed(0) {} + + void push(const ElementType& newData) { + head = (head + size - 1) % size; + data[head] = newData; + if (pushed < size) { pushed++; } + } + + bool find(const ElementType& key, unsigned int* outIndex) { + for (unsigned int i = 0; i < pushed; ++i) { + if (data[(head + i) % size] == key) { + (*outIndex) = i; + return true; + } + } + return false; + } + + const ElementType& operator[](const unsigned int& index) const { + return data[(head + index) % size]; + } +}; + +} \ No newline at end of file diff --git a/App/util/G3DCore.h b/App/util/G3DCore.h new file mode 100644 index 0000000..b18df38 --- /dev/null +++ b/App/util/G3DCore.h @@ -0,0 +1,71 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#ifndef _70F7A2EE1B6E4dd0AF07E4BFA609A3D1 +#define _70F7A2EE1B6E4dd0AF07E4BFA609A3D1 + +#include "G3D/Vector2.h" +#include "G3D/Vector3.h" +#include "G3D/Vector4.h" +#include "G3D/Matrix3.h" +#include "G3D/Matrix4.h" +#include "G3D/Vector3int16.h" +#include "G3D/Vector2int16.h" +#include "G3D/Color4uint8.h" +#include "G3D/Color3uint8.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Plane.h" +#include "G3D/Line.h" +#include "G3D/LineSegment.h" + +#include "G3D/AABox.h" +#include "G3D/Box.h" +#include "RbxG3D/RbxCamera.h" +#include "G3D/Color3.h" +#include "G3D/Color4.h" +#include "G3D/g3dmath.h" +#include "G3D/Rect2D.h" +#include "G3D/Sphere.h" + +#include "G3D/vectorMath.h" +#include "G3D/Debug.h" + +// TODO: this can cause namespace collisions: +//using G3D::Array; + +namespace RBX { + typedef G3D::Vector2 Vector2; + typedef G3D::Vector3 Vector3; + typedef G3D::Vector4 Vector4; + typedef G3D::Vector2int16 Vector2int16; + typedef G3D::Vector3int16 Vector3int16; + typedef G3D::Color4uint8 Color4uint8; + typedef G3D::Color3uint8 Color3uint8; + typedef G3D::Matrix3 Matrix3; + typedef G3D::Matrix4 Matrix4; + typedef G3D::CoordinateFrame CoordinateFrame; + typedef RBX::RbxRay Ray; + typedef G3D::Plane Plane; + typedef G3D::Line Line; + typedef G3D::LineSegment LineSegment; + typedef G3D::Color3 Color3; + typedef G3D::Color4 Color4; + typedef G3D::Rect2D Rect2D; + typedef G3D::Box Box; + typedef G3D::AABox AABox; + typedef G3D::Sphere Sphere; + + enum IntersectResult + { + irNone = 0, + irPartial =1, + irFull = 2 + }; +} + +namespace G3D +{ + std::size_t hash_value(const G3D::Vector3& v); + std::size_t hash_value(const G3D::Vector3int16& v); +} + +#endif \ No newline at end of file diff --git a/App/util/GameMode.h b/App/util/GameMode.h new file mode 100644 index 0000000..02d59a5 --- /dev/null +++ b/App/util/GameMode.h @@ -0,0 +1,12 @@ +/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +namespace RBX { + +namespace Network { + + typedef enum {GAME_SERVER, DPHYS_GAME_SERVER, CLIENT, DPHYS_CLIENT, WATCH_ONLINE, VISIT_SOLO, EDIT, LOCAL_PLAY} GameMode; + +} +} diff --git a/App/util/Guid.h b/App/util/Guid.h new file mode 100644 index 0000000..a790f5a --- /dev/null +++ b/App/util/Guid.h @@ -0,0 +1,255 @@ + +#pragma once + +#include +#include + +#include "util/name.h" +#include "util/Object.h" + +#include "rbx/intrusive_ptr_target.h" +#include + +namespace RBX +{ + // A simple class that is a globally unique identifier + class Guid : boost::noncopyable + { + public: + struct Scope + { + public: + Scope() { setNull(); } + + void setNull() { name = &RBX::Name::getNullName(); } + bool isNull() const { + return *name == RBX::Name::getNullName(); + } + void set( const std::string& s ) + { + name = &RBX::Name::declare(s.c_str()); + } + void set( const char* s ) + { + name = &RBX::Name::declare(s); + } + + const RBX::Name* getName() const { + return name; + } + + int compare(const Scope& other) const + { + return name->compare(*other.name); + } + bool operator ==(const Scope& other) const { + return name->compare(*other.name) == 0; + } + bool operator <(const Scope& other) const { + return name->compare(*other.name) < 0; + } + + static const Scope& null(); + + private: + static Scope nullScope; + + const RBX::Name* name; + }; + + struct Data + { + Scope scope; + int index; + bool operator ==(const Data& other) const; + bool operator <(const Data& other) const; + // For debugging only. A string that is not guaranteed to be unique + std::string readableString(int scopeLength = 4) const; + }; + private: + Data data; + public: + Guid(); + bool operator ==(const Guid& other) const { return data == other.data; } + bool operator <(const Guid& other) const { return data < other.data; } + + // Compare 2 pairs of Guids. a0-a1 and b0-b1 are commutativity. Any item may be NULL + static int compare(const Guid* a, const Guid* b); + static int compare(const Guid* a0, const Guid* a1, const Guid* b0, const Guid* b1); + + // Used for serialization: + void assign(Data data); + void extract(Data &data) const { data = this->data; } + + void copyDataFrom(const Guid& other) { + Data data; + other.extract(data); + this->assign(data); + } + + // For debugging only. A string that is not guaranteed to be unique + std::string readableString(int scopeLength = 4) const { return data.readableString(scopeLength); } + + static const RBX::Guid::Scope& getLocalScope(); + + static void generateRBXGUID(RBX::Guid::Scope& result); + + // Creates a string like this: RBXc200e36038c511ceae6208002b2b79ef + static void generateRBXGUID(std::string& result); + + // Creates a string like this: {c200e360-38c5-11ce-ae62-08002b2b79ef} + static void generateStandardGUID(std::string& result); + }; + + inline size_t hash_value(const Guid::Data& data) + { + size_t result = 0; + boost::hash_combine(result, data.scope.getName()); + boost::hash_combine(result, data.index); + return result; + } + + // A base class for objects that contain a Guid and that want lookup-by-Guid + template + class RBXBaseClass GuidItem + { + public: + // A Registry maintains lookup information for Guids. + // Each instance can belong to only one Registry. + // Usually a Registry is associated with a DataModel + class Registry + : public rbx::quick_intrusive_ptr_target + { + friend class GuidItem; + typedef boost::unordered_map > Map; + Map map; + RBX::mutex mutex; + + Registry() + {} + public: + static boost::intrusive_ptr create() + { + return boost::intrusive_ptr(new Registry()); + } + + ~Registry() + { + RBXASSERT(map.size() ==0); + } + + // Returns true for empty Guid data, false for unregistered Guid data + bool lookupByGuid(const Guid::Data& data, shared_ptr &result) + { + if(data.scope.isNull()) + { + result.reset(); + return true; + } + + RBX::mutex::scoped_lock lock(mutex); + typename Map::const_iterator iter = GuidItem::Registry::map.find(data); + if (iter!=map.end()) + { + result = iter->second.lock(); + } + else + { + result.reset(); + } + return !!result; + } + + shared_ptr getByGuid(const Guid::Data& data) + { + shared_ptr result; + lookupByGuid(data, result); + return result; + } + + // Ensure that this Guid is in the registry (thread-safe) + void registerGuid(const T* item) + { + reg(item); + } + + // Assigns a new guid to item. (not thread-safe) + void assignGuid(T* item, const Guid::Data& guidData) + { + if (item->registry) + item->registry->unregister(item); + + item->guid.assign(guidData); + + reg(item); + + item->onGuidChanged(); + } + + void tryUnregister(GuidItem* item) + { + // In ClientReplicator::streamOutInstance(), we are traverse all the part's descendants and unregister them + // It is possible some descendants have been already GC'd and unregistered earlier from the same GC loop in GCJob::gcRegion() + // Here we simply skip it if the item has already been unregistered. + if (item->registry) + { + unregister(item); + } + } + + void unregister(GuidItem* item) + { + RBXASSERT(item->registry.get()==this); + Guid::Data data; + item->guid.extract(data); + { + RBX::mutex::scoped_lock lock(mutex); + int num = map.erase(data); + RBXASSERT(num == 1); + } + item->registry.reset(); + } + + private: + void reg(const T* item) + { + if (!item->registry) + { + Guid::Data data; + item->guid.extract(data); + RBX::mutex::scoped_lock lock(mutex); + if (!item->registry) // thread-safe check + { + map[data] = weak_from(const_cast(item)); + item->registry = this; + } + } + else + RBXASSERT(item->registry.get()==this); + } + + }; + friend class Registry; + + private: + mutable boost::intrusive_ptr registry; + Guid guid; + + public: + GuidItem() + { + } + + ~GuidItem() + { + if (registry) + registry->unregister(this); + } + + const Guid& getGuid() const + { + return guid; + } + }; +}; + diff --git a/App/util/HTW3C.h b/App/util/HTW3C.h new file mode 100644 index 0000000..c9f614c --- /dev/null +++ b/App/util/HTW3C.h @@ -0,0 +1,9 @@ +#pragma once + +extern "C" { +#ifndef _WIN32 +#include +#endif + +#include +} diff --git a/App/util/Handle.h b/App/util/Handle.h new file mode 100644 index 0000000..3ed640a --- /dev/null +++ b/App/util/Handle.h @@ -0,0 +1,59 @@ + +#ifndef _28C82C86EA754d62AF934FA46C3698ED +#define _28C82C86EA754d62AF934FA46C3698ED + +#include +#include +#include + +#include "rbx/Debug.h" +#include "Util/Object.h" +#include "Util/Memory.h" + +namespace RBX { + + namespace Reflection + { + class DescribedBase; + } + + // Used to reference RBX::Reflection::DescribedBase in the XmlElement class + class InstanceHandle { + shared_ptr target; + public: + InstanceHandle() {} + InstanceHandle(Reflection::DescribedBase* target); + InstanceHandle(shared_ptr target):target(target) {} + InstanceHandle(const InstanceHandle& other):target(other.target) {} + + InstanceHandle& operator=(const InstanceHandle& value) { + target = value.target; + return *this; + } + InstanceHandle& operator=(shared_ptr value) { + target = value; + return *this; + } + + bool empty() const; + + shared_ptr getTarget() const { return target; } + + void linkTo(shared_ptr target); + + bool operator==(const InstanceHandle& other) const { return operatorEqual(other); } + bool operator!=(const InstanceHandle& other) const { return !operatorEqual(other); } + bool operator<(const InstanceHandle& other) const{ return operatorLess(other); } + bool operator>(const InstanceHandle& other) const{ return operatorGreater(other); } + + protected: + bool operatorEqual(const InstanceHandle& other) const; + bool operatorLess(const InstanceHandle& other) const; + bool operatorGreater(const InstanceHandle& other) const; + }; + + + +} + +#endif diff --git a/App/util/Hash.h b/App/util/Hash.h new file mode 100644 index 0000000..e0826b1 --- /dev/null +++ b/App/util/Hash.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +// A simple hash function from Robert Sedgwicks Algorithms in C book. + +namespace RBX { + + class Hash { + public: + static unsigned int hash(const void* data, size_t bytes); + static unsigned int hash(const std::string& str); + + static void hashAppend(unsigned int& currentHash, const void* data, size_t bytes); + static void hashAppend(unsigned int& currentHash, unsigned int append); + }; + +} // namespace \ No newline at end of file diff --git a/App/util/HeapValue.h b/App/util/HeapValue.h new file mode 100644 index 0000000..62da17e --- /dev/null +++ b/App/util/HeapValue.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include "ObscureValue.h" +#include "FastLog.h" + + +namespace RBX { + +// Wrapper around boost::scoped_ptr that allows it to be used like +// a normal reference to the type (i.e. T& instead of T*). +// Also mildly obscures stored values so that they are harder to find +// with a memory scan. +template class HeapValue { + boost::scoped_ptr > storage; +public: + explicit HeapValue(const T& value) : storage(new ObscureValue(value)) { + } + + operator const T() const { + return *storage; + } + + HeapValue& operator=(const T& other) { + *storage = other; + return *this; + } +private: + // Disable no-arg construction, copy, and regular assign. + // Some of these may be safe, but they are not needed yet, + // and the safety of this class is easier to understand without + // them. + HeapValue(); + HeapValue(const HeapValue&); + HeapValue& operator=(const HeapValue&); +}; + +} \ No newline at end of file diff --git a/App/util/HeartbeatInstance.h b/App/util/HeartbeatInstance.h new file mode 100644 index 0000000..55d9bf4 --- /dev/null +++ b/App/util/HeartbeatInstance.h @@ -0,0 +1,35 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "rbx/signal.h" +#include "Util/G3DCore.h" + +// hook up by overriding onServiceProvider call in this pattern: +// +// /*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider) { +// Super::onServiceProvider(oldProvider, newProvider); +// onServiceProviderHeartbeatInstance(oldProvider, newProvider); // hooks up heartbeat +// } +// + +namespace RBX { + class Heartbeat; + class ServiceProvider; + + class HeartbeatInstance + { + private: + rbx::signals::scoped_connection heartbeatConnection; + + protected: + // call this inside onServiceProvider + void onServiceProviderHeartbeatInstance(ServiceProvider* oldProvider, ServiceProvider* newProvider); + + /*implement*/ virtual void onHeartbeat(const Heartbeat& event) = 0; + + public: + HeartbeatInstance() {} + virtual ~HeartbeatInstance() {} + }; +} // namespace RBX diff --git a/App/util/HitTest.h b/App/util/HitTest.h new file mode 100644 index 0000000..60e8a8a --- /dev/null +++ b/App/util/HitTest.h @@ -0,0 +1,37 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/G3DCore.h" +#include "Util/NormalId.h" +#include "appdraw/HandleType.h" + +namespace RBX { + + class Extents; + + class HandleHitTest + { + public: + static bool hitTestHandleLocal( const Extents& localExtents, + const CoordinateFrame& location, + HandleType handleType, + const Ray& gridRay, + Vector3& hitPointWorld, + NormalId& localNormalId, + const int normalIdMask = NORM_ALL_MASK); + + static bool hitTestHandleWorld( const Extents& worldExtents, + HandleType handleType, + const Ray& gridRay, + Vector3& hitPointWorld, + NormalId& worldNormalId, + const int normalIdMask = NORM_ALL_MASK); + + static bool hitTestMoveHandleWorld(const Extents& worldExtents, + const RbxRay& gridRay, + Vector3& hitPointWorld, + NormalId& worldNormalId, + const int normalIdMask = NORM_ALL_MASK); + }; +} // namespace RBX \ No newline at end of file diff --git a/App/util/HitTestFilter.h b/App/util/HitTestFilter.h new file mode 100644 index 0000000..a04665b --- /dev/null +++ b/App/util/HitTestFilter.h @@ -0,0 +1,22 @@ +#pragma once + +namespace RBX { + + class Primitive; + + // Fail: Stop the hit test - don't bore any further down + // Ignore: Keep testing + // Hit: Found something + + class HitTestFilter { + public: + typedef enum Result { STOP_TEST, + IGNORE_PRIM, + INCLUDE_PRIM} Result; + + virtual Result filterResult(const Primitive* testMe) const = 0; + virtual ~HitTestFilter() + {} + }; + +} // namespace \ No newline at end of file diff --git a/App/util/Http.h b/App/util/Http.h new file mode 100644 index 0000000..522fe66 --- /dev/null +++ b/App/util/Http.h @@ -0,0 +1,229 @@ +#pragma once + +#include + +#include + +#include "rbx/atomic.h" +#include "rbx/CEvent.h" +#include "rbx/RunningAverage.h" +#include "rbx/rbxTime.h" +#include "util/HttpAux.h" + +DYNAMIC_FASTFLAG(UseAssetTypeHeader) + +namespace RBX +{ + namespace HttpCache + { + enum Policy + { + // No caching by default. + PolicyDefault, + + // Cache based on the final URL (after all 302s). + PolicyFinalRedirect, + }; + } // namespace HttpCache + + class mutex; + + class http_status_error: + public std::runtime_error + { + public: + int statusCode; + http_status_error(int statusCode); + http_status_error(int statusCode, const std::string& message); + }; + + class Http + { + public: + typedef enum { Uninitialized=-1 ,WinInet=0, WinHttp=1, XboxHttp=2 } API; + + enum CookieSharingPolicy + { + CookieSharingUndefined = 0x0, + CookieSharingMultipleProcessesRead = 0x1, + CookieSharingMultipleProcessesWrite = 0x2, + CookieSharingSingleProcessMultipleThreads = 0x4, + }; + + inline friend CookieSharingPolicy operator|(CookieSharingPolicy a, CookieSharingPolicy b) + { + return static_cast(static_cast(a) | static_cast(b)); + } + + inline friend CookieSharingPolicy operator|=(CookieSharingPolicy a, CookieSharingPolicy b) + { + return static_cast(static_cast(a) | static_cast(b)); + } + + static std::string accessKey; + static std::string gameSessionID; // additional header to be sent in POST requests to roblox + static std::string gameID; + static std::string placeID; + static std::string requester; + static std::string rbxUserAgent; + + static int playerCount; + + static bool useDefaultTimeouts; + + // Defined in Utilities.cpp. + static const std::string kGameSessionHeaderKey; + static const std::string kGameIdHeaderKey; + static const std::string kPlaceIdHeaderKey; + static const std::string kRequesterHeaderKey; + static const std::string kPlayerCountHeaderKey; + static const std::string kAccessHeaderKey; + static const std::string kAssetTypeKey; + static const std::string kRBXAuthenticationNegotiation; + static const std::string kContentTypeDefaultUnspecified; + static const std::string kContentTypeUrlEncoded; + static const std::string kContentTypeApplicationJson; + static const std::string kContentTypeApplicationXml; + static const std::string kContentTypeTextPlain; + static const std::string kContentTypeTextXml; + + private: + static API defaultApi; + static CookieSharingPolicy cookieSharingPolicy; + + std::string alternateUrl; // Used if a CDN fails to deliver and we can try an alternate URL for gets + + HttpCache::Policy cachePolicy; + + int connectTimeoutMillis; + int responseTimeoutMillis; + int sendTimeoutMillis; + int dataSendTimeoutMillis; + + API instanceApi; + + static RBX::mutex *robloxResponceLock; + static RBX::mutex *cdnResponceLock; + static std::string lastCsrfToken; + static boost::mutex lastCsrfTokenMutex; + + class MutexGuard + { + public: + MutexGuard(); + ~MutexGuard(); + }; + + static MutexGuard lockGuard; + + void init(); + public: + static void init(API api, CookieSharingPolicy cookieSharingPolicy); + static void SetUseStatistics(bool value); + static void SetUseCurl(bool value); + static rbx::atomic cdnSuccessCount; + static rbx::atomic cdnFailureCount; + static rbx::atomic alternateCdnSuccessCount; + static rbx::atomic alternateCdnFailureCount; + static double lastCdnFailureTimeSpan; + static rbx::atomic robloxSuccessCount; + static rbx::atomic robloxFailureCount; + + static WindowAverage robloxResponce; + static WindowAverage cdnResponce; + + static RBX::mutex *getRobloxResponceLock(); + static RBX::mutex *getCdnResponceLock(); + + std::string url; + Http():instanceApi(defaultApi),url("") { init(); } + Http(const char* url):instanceApi(defaultApi),url(url) { init(); } + Http(const char* url, API api):instanceApi(api),url(url) { init(); } + Http(const std::string& url):instanceApi(defaultApi),url(url) { init(); } + Http(const std::string& url, API api):instanceApi(api),url(url) { init(); } + + bool recordStatistics; + bool shouldRetry; + HttpAux::AdditionalHeaders additionalHeaders; + bool doNotUseCachedResponse; + std::string authDomainUrl; + void setAuthDomain(std::string domain) + { + authDomainUrl = domain; + additionalHeaders[kRBXAuthenticationNegotiation] = domain; + } + void setExpectedAssetType(const std::string& type) + { + if (DFFlag::UseAssetTypeHeader && !type.empty()) + additionalHeaders[kAssetTypeKey] = type; + } + + static void setCookiesForDomain(const std::string& domain, const std::string& cookies); + static void getCookiesForDomain(const std::string& domain, std::string& cookies); + + void setResponseTimeout(int timeout) { responseTimeoutMillis = timeout; } + void setSendTimeout(int timeout) { sendTimeoutMillis = timeout; } + void setDataSendTimeout(int timeout) { dataSendTimeoutMillis = timeout; } + void setConnectionTimeout(int timeout) { connectTimeoutMillis = timeout; } + void setCachePolicy(const HttpCache::Policy policy) { cachePolicy = policy; } + + // Async + void post(const std::string& input, const std::string& contentType, bool compress, boost::function handler, bool externalRequest = false); + void post(boost::shared_ptr input, const std::string& contentType, bool compress, boost::function handler, bool externalRequest = false); + void get(boost::function handler, bool allowExternal = false); + + // Sync + void post(std::istream& input, const std::string& contentType, bool compress, std::string& response, bool externalRequest = false); + void get(std::string& response, bool allowExternal = false); + + static bool isExternalRequest(const char* url); + static bool trustCheck(const char* url, bool allowExternal = false); + static bool trustCheckBrowser(const char* url); + static bool isScript(const char* url); + static bool isRobloxSite(const char* url); + static bool isStrictlyRobloxSite(const char* url); + static bool isMoneySite(const char* url); + + // Utility + static std::string urlEncode(const std::string& s); + // urlDecode is only tested to work on strings produced from urlEncode + static std::string urlDecode(const std::string& fragment); + + void applyAdditionalHeaders(RBX::HttpAux::AdditionalHeaders& outHeaders); + + private: + void httpGetPost(bool isPost, std::istream& dataStream, const std::string& contentType, bool compressData, const HttpAux::AdditionalHeaders& additionalHeaders, bool allowExternal, std::string& response, bool forceNativeHttp = false); +#if defined(RBX_PLATFORM_DURANGO) + void httpGetPostXbox(bool isPost, std::istream& dataStream, const std::string& contentType, bool compressData, const HttpAux::AdditionalHeaders& additionalHeaders, bool allowExternal, HttpCache::Policy cachePolicy, std::string& response); +#elif defined(_WIN32) + void httpGetPostWinInet(bool isPost, std::istream& dataStream, const std::string& contentType, bool compressData, const HttpAux::AdditionalHeaders& additionalHeaders, bool allowExternal, std::string& response); + void httpGetPostWinHttp(bool isPost, std::istream& dataStream, const std::string& contentType, bool compressData, const HttpAux::AdditionalHeaders& additionalHeaders, bool allowExternal, std::string& response); +#elif defined(__APPLE__) + void httpGetPostImpl(bool isPost, std::istream& dataStream, const std::string& contentType, bool compressData, const HttpAux::AdditionalHeaders& additionalHeaders, bool allowExternal, std::string& response); +#endif + bool doHttpGetPostWithNativeFallbackForReporting(bool isPost, std::istream& dataStream, const std::string& contentType, bool compressData, const HttpAux::AdditionalHeaders& additionalHeaders, bool allowExternal, std::string& response); + + void ThrowIfFailure(bool success, const char* message); + +#ifdef _WIN32 + static void setCookiesForDomainWinInet(const std::string& domain, const std::string& cookies); +#endif + public: +#ifdef _WIN32 + void onWinHttpRedirect(unsigned long dwInternetStatus, std::string redirectUrl); +#endif + + static void ThrowIfFailure(bool success, const char* url, const char* message); + +#if defined(_WIN32) && !defined(RBX_PLATFORM_DURANGO) + static void ThrowLastError(int error, const char* url, const char* message); +#endif + + static void setProxy(const std::string& host, long port = 0); + + // These methods are not safe to be called from static initializers, because they rely on + // static member variables (which don't have order guarantees wrt other static initializers). + static std::string getLastCsrfToken(); + static void setLastCsrfToken(const std::string& newToken); + }; +} diff --git a/App/util/HttpAsync.h b/App/util/HttpAsync.h new file mode 100644 index 0000000..931fb1c --- /dev/null +++ b/App/util/HttpAsync.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#include +#include +#include "util/HttpAux.h" + +namespace RBX +{ + typedef boost::shared_future HttpFuture; + + class HttpOptions + { + friend class HttpAsync; + + public: + HttpOptions() + : external(false) + , doNotUseCachedResponse(false) + { + } + + void addHeader(const std::string& key, const std::string& value); + void setExternal(bool value); + void setDoNotUseCachedResponse(); + + private: + HttpAux::AdditionalHeaders headers; + bool external; + bool doNotUseCachedResponse; + }; + + class HttpPostData + { + friend class HttpAsync; + + public: + HttpPostData(const std::string& contents, const std::string& contentType, bool compress); + HttpPostData(const boost::shared_ptr& contents, const std::string& contentType, bool compress); + + private: + boost::shared_ptr data; + std::string contentType; + bool compress; + }; + + class HttpAsync + { + public: + static HttpFuture get(const std::string& url, const HttpOptions& options = HttpOptions()); + static HttpFuture getWithRetries(const std::string& url, int retryCount, const HttpOptions& options = HttpOptions()); + + static HttpFuture post(const std::string& url, const HttpPostData& postData, const HttpOptions& options = HttpOptions()); + }; +} diff --git a/App/util/HttpAux.h b/App/util/HttpAux.h new file mode 100644 index 0000000..7203201 --- /dev/null +++ b/App/util/HttpAux.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace RBX +{ +namespace HttpAux +{ +typedef boost::unordered_map AdditionalHeaders; +} // namespace HttpAux +} // namespace RBX diff --git a/App/util/HttpPlatformImpl.h b/App/util/HttpPlatformImpl.h new file mode 100644 index 0000000..fd40de9 --- /dev/null +++ b/App/util/HttpPlatformImpl.h @@ -0,0 +1,207 @@ +#pragma once +#define _CRT_SECURE_NO_WARNINGS 1 + +#include "util/Http.h" +#include "util/HttpAux.h" +#include "util/FileSystem.h" +#include "util/NamedMutex.h" +#include "rbx/Debug.h" + +#include + +namespace RBX +{ +namespace HttpPlatformImpl +{ +namespace Cache +{ +// Given a url, provide hashed file location on disk. +boost::filesystem::path cacheFilePath(const char* url); + +struct Header +{ +#define RBX_CACHE_FILE_MAGIC 0x52425848 // RBXH +#define RBX_CACHE_FILE_VERSION 0x1 +#define RBX_CACHE_URL_MAX_LENGTH 1024U + + const uint32_t magic; + const uint32_t version; + const uint32_t urlBytes; + const uint8_t url[RBX_CACHE_URL_MAX_LENGTH]; // not null-terminated + const uint32_t responseCode; + const uint32_t responseHeadersSize; + const uint32_t responseHeadersHash; + const uint32_t responseBodySize; + const uint32_t responseBodyHash; + const uint32_t reserved; +}; + +class Data +{ + const uint8_t* underlying; + const size_t bytes; + +public: + Data(const uint8_t* data, const size_t bytes) : + underlying(data), bytes(bytes) + {} + + Data(const char* data, const size_t bytes) : + underlying(reinterpret_cast(data)), bytes(bytes) + { + RBXASSERT(sizeof(char) == sizeof(uint8_t)); + } + + uint8_t operator[](size_t index) const + { + return underlying[index]; + } + + const uint8_t* data() const + { + return underlying; + } + + std::string toString() const + { + std::string result; + result.assign(underlying, underlying+bytes); + return result; + } + + size_t size() const + { + return bytes; + } +}; + +// The data in this structure is read-only, and if you +// want to update the underlying data, you must do it via +// the update() method. +struct CacheEntry // Word-aligned data structure +{ + Header cacheHeader; + uint8_t data[1]; + + const Data getResponseHeader() const; + const Data getResponseBody() const; + + CacheEntry(const Header& cacheHeader, const Data& responseHeaders, const Data& responseBody); +}; + +class CacheResult +{ + boost::shared_ptr cacheEntry; + size_t cacheSize; + const std::string invalidReason; + +public: + explicit CacheResult(const std::string& invalidReason) : invalidReason(invalidReason) + {} + + explicit CacheResult( shared_ptr entry, size_t size) + : cacheEntry(entry), cacheSize(size) + { + RBXASSERT(size); + } + + bool isValid() const + { + return NULL != cacheEntry; + } + + const std::string& getInvalidReason() const + { + return invalidReason; + } + + const Header& getCacheHeader() const + { + return cacheEntry->cacheHeader; + } + + const Data getResponseHeader() const + { + return cacheEntry->getResponseHeader(); + } + const Data getResponseBody() const + { + return cacheEntry->getResponseBody(); + } + + const size_t size() const + { + return cacheSize; + } + + + // Converts the URL to a known location on disk and tries to open and return that file. + // Returns NULL if file could not be opened. + static CacheResult open(const char* assetUrl, const char* cdnUrl); + + // Atomically update the file with new data and returned a new CacheEntry to it. + static CacheResult update(const char* assetUrl, const char* cdnUrl, const uint32_t responseCode, const Data& headers, const Data& body); +}; + +struct CacheCleanOptions +{ + size_t numFilesRequiredBeforeCleaning; + size_t numFilesToKeep; + size_t numGigaBytesAvailableTrigger; + bool flagCleanUpBasedOnMemory; +}; + +void cleanCache(const CacheCleanOptions& options); +} // namespace Cache + +struct HttpOptions +{ + // Basic data + const std::string& url; + bool externalRequest; + HttpCache::Policy cachePolicy; + + // Connection handling information + long connectTimeoutMillis; + long performTimeoutMillis; + + // Post data + std::istream* postData; + bool compressedPostData; + + // Header data + std::string const* hdrContentType; + HttpAux::AdditionalHeaders const* addlHeaders; + + HttpOptions(const std::string& url, bool externalRequest, HttpCache::Policy cachePolicy, long connectTimeoutMillis, long performTimeoutMillis) + :url(url) + ,externalRequest(externalRequest) + ,cachePolicy(cachePolicy) + ,connectTimeoutMillis(connectTimeoutMillis) + ,performTimeoutMillis(performTimeoutMillis) + ,postData(NULL) + ,hdrContentType(NULL) + ,addlHeaders(NULL) + {} + + void setPostData(std::istream* dataStream, bool compressed) + { + postData = dataStream; + compressedPostData = compressed; + } + + void setHeaders(const std::string* contentType, const HttpAux::AdditionalHeaders* headers) + { + hdrContentType = contentType; + addlHeaders = headers; + } +}; // struct HttpOptions + +void init(Http::CookieSharingPolicy cookieSharingPolicy); // NOTE: This call is not thread-safe. +void setCookiesForDomain(const std::string& domain, const std::string& cookies); +void getCookiesForDomain(const std::string& domain, std::string& cookies); +boost::filesystem::path getRobloxCookieJarPath(); +void setProxy(const std::string& host, long port = 0); +void perform(HttpOptions& options, std::string& response); +} // namespace HttpPlatformImpl +} // namespace RBX diff --git a/App/util/IHasLocation.h b/App/util/IHasLocation.h new file mode 100644 index 0000000..fa36b2e --- /dev/null +++ b/App/util/IHasLocation.h @@ -0,0 +1,24 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/G3DCore.h" +#include "rbx/Declarations.h" + +namespace RBX { + +// +// http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.10 + + // This is a virtual base class - see note above. Any object that descends from it + // should use the "virtual" keyword, so only one is included. + // + class RBXInterface IHasLocation + { + public: + virtual const CoordinateFrame getLocation() = 0; + + virtual ~IHasLocation() {} + }; + +} // namespace diff --git a/App/util/IMetric.h b/App/util/IMetric.h new file mode 100644 index 0000000..c2f89e1 --- /dev/null +++ b/App/util/IMetric.h @@ -0,0 +1,20 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "rbx/Debug.h" + +// Simple class for returning metric values - used for graphics reporting + +namespace RBX { + + class RBXInterface IMetric + { + public: + IMetric() {} + virtual ~IMetric() {} + + virtual std::string getMetric(const std::string& metric) const = 0; + virtual double getMetricValue(const std::string& metric) const = 0; + }; +} // namespace diff --git a/App/util/IndexArray.h b/App/util/IndexArray.h new file mode 100644 index 0000000..8b30aa9 --- /dev/null +++ b/App/util/IndexArray.h @@ -0,0 +1,154 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "boost/utility.hpp" +#include "util/utilities.h" +#include "Util/G3DCore.h" +#include "rbx/Debug.h" +#include "G3D/Array.h" + +namespace RBX { + +/* USAGE + class C + { + private: + int index; + + public: + C() : index(-1) {} + ~C() {RBXASSERT(index == -1);} + + int& getIndex() {return index;} + }; + + + main() + { + IndexArray array; + + C* c = new C(); + C c2; + + array.fastAppend(c); + array.fastAppend(&c2); + + array.fastRemove(c) + array.fastRemove(&c2); + } +*/ + + template + class IndexArray + : public boost::noncopyable // You can't copy elements from one array to another + // since the index values can't be shared. + { + private: + G3D::Array array; + + int& indexOf(Item* item) const { + return (item->*getIndex)(); + } + + public: + typedef Item** Iterator; + typedef const Item** ConstIterator; + + inline void fastAppend(Item* item) + { + RBXASSERT(item); + RBXASSERT(indexOf(item) == -1); + RBXASSERT_IF_VALIDATING(array.find(item) == array.end()); + + indexOf(item) = array.size(); + array.append(item); + } + + inline void fastRemove(Item* item) + { + RBXASSERT_IF_VALIDATING(array.find(item) != array.end()); + + int removeIndex = indexOf(item); + + RBXASSERT(removeIndex >= 0); + RBXASSERT(array[removeIndex] == item); + + // Move last item to removal index + Item* oldLast = array.last(); // if array size == 1, this is redundant + array[removeIndex] = oldLast; + indexOf(oldLast) = removeIndex; + array.pop(false); + + // Update indices + indexOf(item) = -1; + } + + inline void remove(Item* item) + { + RBXASSERT_IF_VALIDATING(array.find(item) != array.end()); + + int removeIndex = indexOf(item); + + RBXASSERT(removeIndex >= 0); + RBXASSERT(array[removeIndex] == item); + + // Move all the items back in the array. + for (int i = removeIndex; i < array.size() - 1; i++) + { + Item* nextItem = array[i + 1]; + array[i] = nextItem; + indexOf(nextItem) = i; + } + + array.pop(false); + + // Update indices + indexOf(item) = -1; + } + + inline bool fastContains(Item* item) const + { + bool answer = (indexOf(item) >= 0); + RBXASSERT_IF_VALIDATING(answer == underlyingArray().contains(item)); + return answer; + } + + G3D::Array& underlyingArray() { + return array; + } + + const G3D::Array& underlyingArray() const { + return array; + } + + inline Item* operator[](int n) { + RBXASSERT(indexOf(array[n]) == n); + return array[n]; + } + + inline Item* operator[](unsigned int n) { + RBXASSERT(indexOf(array[n]) == n); + return array[n]; + } + + inline Item* const operator[](int n) const { // the pointer is const? + RBXASSERT(indexOf(array[n]) == n); + return array[n]; + } + + inline Item* const operator[](unsigned int n) const { // the pointer is const?... + RBXASSERT(indexOf(array[n]) == n); + return array[n]; + } + + inline int size() const { + return array.size(); + } + + }; + +}// namespace + + + diff --git a/App/util/IndexBox.h b/App/util/IndexBox.h new file mode 100644 index 0000000..1caa454 --- /dev/null +++ b/App/util/IndexBox.h @@ -0,0 +1,108 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ +#pragma once + +#include "Util/G3DCore.h" + + +namespace RBX { + +class IndexBox { + +private: + /** + Looking from positive X back through to negative X, z is left, y is up + 0 1 4 5 + + 2 3 6 7 + front back (seen through front) + */ + + Vector3 corner[8]; +public: + + IndexBox(); + IndexBox( + const Vector3& min, + const Vector3& max); + + virtual ~IndexBox() {} + + Vector3 getCenter() const; + + Vector3 getCorner(int i) const { + return corner[i]; + } + + Vector3 getFaceNormal(int f) const { + return Vector3( INDEXBOX_FACE_TO_NORMAL[f][0], + INDEXBOX_FACE_TO_NORMAL[f][1], + INDEXBOX_FACE_TO_NORMAL[f][2]); + } + + Vector3 getEdgeNormal(int f, int e) const { + return getFaceNormal(INDEXBOX_FACE_EDGE_TO_NORMAL[f][e]); + } + + /** + Returns the four corners of a face (0 <= f < 6). + The corners are returned to form a counter clockwise quad facing outwards. + */ + void getFaceCorners( + int f, + Vector3& v0, + Vector3& v1, + Vector3& v2, + Vector3& v3) const; + + /** The edge travels from v0 to v1. nR is to the right and nL is to the left.*/ + void getEdge( + int e, + Vector3& v0, + Vector3& v1, + Vector3& nL, + Vector3& nR) const; + + void getEdge( + int e, + Vector4& v0, + Vector4& v1, + Vector3& nL, + Vector3& nR) const; + + static void getTextureCornersCentered( + int f, + const Vector3& halfSize, + Vector2& t0, + Vector2& t1, + Vector2& t2, + Vector2& t3); + + static void getTextureCornersGrid( + int f, + const Vector3& halfSize, + Vector2& t0, + Vector2& t1, + Vector2& t2, + Vector2& t3); + /** + Returns true if this IndexBox is culled by the provided set of + planes. The IndexBox is culled if there exists at least one plane + whose halfspace the entire IndexBox is not in. + */ + bool culledBy( + const Plane* plane, + int numPlanes) const; + + + bool contains( + const Vector3& point) const; + + static const int INDEXBOX_FACE_TO_VERTEX[6][4]; + static const float INDEXBOX_FACE_TO_NORMAL[6][3]; + static const int INDEXBOX_FACE_EDGE_TO_NORMAL[6][4]; + + /** normal indices are into INDEXBOX_FACE_TO_NORMAL array */ + static const int INDEXBOX_EDGE_TO_VERTEX_AND_NORMALS[12][4]; +}; + +} // namespace diff --git a/App/util/IndexedMesh.h b/App/util/IndexedMesh.h new file mode 100644 index 0000000..77273d3 --- /dev/null +++ b/App/util/IndexedMesh.h @@ -0,0 +1,138 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/IndexedTree.h" + +/* + used ad the basis for the Primitive / Clump / Assembly / Mechanism / SimJob data structures + + Up: Parent + Down: Child + Right: Upper + Left: Lower + + Primitive <-> Clump <-> Assembly <-> Mechanism + A + | + Primitive + A A + | | + Primitive + A + | + Primitive <-> Clump A + A | + | + Primitive + A A + | | + Primitive + A + | + Primitive <-> Clump <-> Assembly + +*/ + + +namespace RBX { + + class IndexedMesh : public IndexedTree + { + private: + IndexedMesh* upper; // could be null - only exists if direct connection to upper + IndexedMesh* lower; // could be null - only exists if direct connection to upper + IndexedMesh* computedUpper; // should == computeUpper; + + protected: + // TODO: Make these protected + IndexedMesh* getUpper() {return upper;} + IndexedMesh* getLower() {return lower;} + + const IndexedMesh* getConstUpper() const {return upper;} + const IndexedMesh* getConstLower() const {return lower;} + + private: + const IndexedMesh* computeParentFromLower() const; + + void setComputedUpper(IndexedMesh* newComputedUpper); + void setLower(IndexedMesh* newLower); + + void severeChildren(IndexedMesh* lowerChild); + void attachChildren(IndexedMesh* lowerChild); + + void lowersChanged() { // cycles up to the parent, calling onLowersChanged(); + onLowersChanged(); + if (getTypedParent()) { + getTypedParent()->lowersChanged(); + } + if (getUpper()) { + getUpper()->lowersChanged(); + } + } + static IndexedMesh* computeUpper(IndexedMesh* lower); + static const IndexedMesh* computeConstUpper(const IndexedMesh* lower); + + void onLowerChildRemoved(IndexedMesh* lowerChild); + void onLowerChildAdded(IndexedMesh* lowerChild); + + ////////////////////////////////////////////////// + // + // Indexed Tree + /*override*/ void onParentChanged(IndexedTree* oldParent); + + protected: + /*implement*/ virtual void onLowersChanged() {} + + public: + IndexedMesh(); + IndexedMesh(IndexedMesh* lower, IndexedMesh* parent); + + ~IndexedMesh(); + + IndexedMesh* getIndexedMeshParent(); // same as getTypedParent, but with bug checking + const IndexedMesh* getConstIndexedMeshParent() const; // same as getTypedParent, but with bug checking + + void setUpper(IndexedMesh* newUpper); + + template + Type* getTypedLower() { + return rbx_static_cast(getLower()); + } + + template + const Type* getConstTypedLower() const { + return rbx_static_cast(getConstLower()); + } + + template + Type* getTypedUpper() { + return rbx_static_cast(getUpper()); + } + + template + const Type* getConstTypedUpper() const { + return rbx_static_cast(getConstUpper()); + } + + IndexedMesh* getComputedUpper(); + const IndexedMesh* getConstComputedUpper() const; + + static bool isUpperRoot(const IndexedMesh* lower); + + template + inline void visitMeAndChildrenWhileNoUpper(Func func) // hack - for iterating all assemblies in a mechanism + { + Type* t = rbx_static_cast(this); + func(t); + for (int i = 0; i < numChildren(); ++i) { + Type* child = getTypedChild(i); + IndexedMesh* childUpper = child->getUpper(); + if (!childUpper) + { + child->visitMeAndChildrenWhileNoUpperUppers(func); + } + } + } + }; +} // namespace diff --git a/App/util/IndexedTree.h b/App/util/IndexedTree.h new file mode 100644 index 0000000..148d18e --- /dev/null +++ b/App/util/IndexedTree.h @@ -0,0 +1,132 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "rbx/Declarations.h" +#include "Util/IndexArray.h" +#include "rbx/Debug.h" + +namespace RBX { + + class RBXBaseClass IndexedTree + { + private: + IndexedTree* parent; + + int index; + int& getIndex() {return index;} + + IndexArray children; + + bool circularReference(IndexedTree* newAncestor, IndexedTree* child); + + protected: + virtual void onParentChanging() {} + virtual void onParentChanged(IndexedTree* oldParent) {} + virtual void onChildAdding(IndexedTree* child) {} + virtual void onChildAdded(IndexedTree* child) {} + virtual void onChildRemoving(IndexedTree* child) {} + virtual void onChildRemoved(IndexedTree* child) {} + virtual void onAncestorChanged() {} + + void setIndexedTreeParent(IndexedTree* newParent); + + public: + IndexedTree(); + + virtual ~IndexedTree(); + + int numChildren() const {return children.size();} + + template + Type* getTypedChild(int i) { + return rbx_static_cast(children[i]); + } + + template + const Type* getConstTypedChild(int i) const { + return rbx_static_cast(children[i]); + } + + template + Type* getTypedParent() { + return rbx_static_cast(parent); + } + + template + const Type* getConstTypedParent() const { + return rbx_static_cast(parent); + } + + template + Type* getRoot() { + return (parent) + ? parent->getRoot() + : rbx_static_cast(this); + } + + template + const Type* getRoot() const { + return (parent) + ? parent->getRoot() + : rbx_static_cast(this); + } + + template + Type* getOneBelowRoot() { + IndexedTree* above = parent; + RBXASSERT(above != NULL); + IndexedTree* answer = this; + while (above->parent) { + answer = above; + above = above->parent; + } + return rbx_static_cast(answer); + } + + + + int getDepth() const { + return parent ? (parent->getDepth() + 1) : 1; + } + + template + inline void visitMeAndChildren(Func func) + { + Type* t = rbx_static_cast(this); + func(t); + for (int i = 0; i < children.size(); ++i) { + children[i]->visitMeAndChildren(func); + } + } + + template + inline void visitConstMeAndChildren(Func func) + { + const Type* t = rbx_static_cast(this); + func(t); + for (int i = 0; i < children.size(); ++i) { + children[i]->visitConstMeAndChildren(func); + } + } + + template + inline void visitDescendents(Func func) + { + for (int i = 0; i < children.size(); ++i) { + children[i]->visitMeAndChildren(func); + } + } + + template + inline void visitConstDescendents(Func func) const + { + for (int i = 0; i < children.size(); ++i) { + children[i]->visitConstMeAndChildren(func); + } + } + + }; + +} // namespace + diff --git a/App/util/InsertMode.h b/App/util/InsertMode.h new file mode 100644 index 0000000..6d9f1b8 --- /dev/null +++ b/App/util/InsertMode.h @@ -0,0 +1,14 @@ +#pragma once + +// A simple hash function from Robert Sedgwicks Algorithms in C book. + +namespace RBX { + + typedef enum {INSERT_RAW, INSERT_TO_TREE, INSERT_TO_3D_VIEW} InsertMode; + + typedef enum { + PUT_TOOL_IN_STARTERPACK, // The user has been prompted about putting a tool into the starter pack + SUPPRESS_PROMPTS} + PromptMode; + +} // namespace \ No newline at end of file diff --git a/App/util/KeyCode.h b/App/util/KeyCode.h new file mode 100644 index 0000000..1dfc585 --- /dev/null +++ b/App/util/KeyCode.h @@ -0,0 +1,326 @@ +#pragma once + +// This code was adapted from SDL via GNU license below + +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +namespace RBX { + + enum KeyCode { + SDLK_UNKNOWN = 0, + SDLK_BACKSPACE = 8, + SDLK_TAB = 9, + SDLK_CLEAR = 12, + SDLK_RETURN = 13, + SDLK_PAUSE = 19, + SDLK_ESCAPE = 27, + SDLK_SPACE = 32, + SDLK_EXCLAIM = 33, + SDLK_QUOTEDBL = 34, + SDLK_HASH = 35, + SDLK_DOLLAR = 36, + SDLK_PERCENT = 37, + SDLK_AMPERSAND = 38, + SDLK_QUOTE = 39, + SDLK_LEFTPAREN = 40, + SDLK_RIGHTPAREN = 41, + SDLK_ASTERISK = 42, + SDLK_PLUS = 43, + SDLK_COMMA = 44, + SDLK_MINUS = 45, + SDLK_PERIOD = 46, + SDLK_SLASH = 47, + SDLK_0 = 48, + SDLK_1 = 49, + SDLK_2 = 50, + SDLK_3 = 51, + SDLK_4 = 52, + SDLK_5 = 53, + SDLK_6 = 54, + SDLK_7 = 55, + SDLK_8 = 56, + SDLK_9 = 57, + SDLK_COLON = 58, + SDLK_SEMICOLON = 59, + SDLK_LESS = 60, + SDLK_EQUALS = 61, + SDLK_GREATER = 62, + SDLK_QUESTION = 63, + SDLK_AT = 64, + /* + Skip uppercase letters + */ + SDLK_LEFTBRACKET = 91, + SDLK_BACKSLASH = 92, + SDLK_RIGHTBRACKET = 93, + SDLK_CARET = 94, + SDLK_UNDERSCORE = 95, + SDLK_BACKQUOTE = 96, + SDLK_a = 97, + SDLK_b = 98, + SDLK_c = 99, + SDLK_d = 100, + SDLK_e = 101, + SDLK_f = 102, + SDLK_g = 103, + SDLK_h = 104, + SDLK_i = 105, + SDLK_j = 106, + SDLK_k = 107, + SDLK_l = 108, + SDLK_m = 109, + SDLK_n = 110, + SDLK_o = 111, + SDLK_p = 112, + SDLK_q = 113, + SDLK_r = 114, + SDLK_s = 115, + SDLK_t = 116, + SDLK_u = 117, + SDLK_v = 118, + SDLK_w = 119, + SDLK_x = 120, + SDLK_y = 121, + SDLK_z = 122, + + SDLK_LEFTCURLY = 123, + SDLK_PIPE = 124, + SDLK_RIGHTCURLY = 125, + SDLK_TILDE = 126, + SDLK_DELETE = 127, + /* End of ASCII mapped keysyms */ + + /* International keyboard syms */ + SDLK_WORLD_0 = 160, /* 0xA0 */ + SDLK_WORLD_1 = 161, + SDLK_WORLD_2 = 162, + SDLK_WORLD_3 = 163, + SDLK_WORLD_4 = 164, + SDLK_WORLD_5 = 165, + SDLK_WORLD_6 = 166, + SDLK_WORLD_7 = 167, + SDLK_WORLD_8 = 168, + SDLK_WORLD_9 = 169, + SDLK_WORLD_10 = 170, + SDLK_WORLD_11 = 171, + SDLK_WORLD_12 = 172, + SDLK_WORLD_13 = 173, + SDLK_WORLD_14 = 174, + SDLK_WORLD_15 = 175, + SDLK_WORLD_16 = 176, + SDLK_WORLD_17 = 177, + SDLK_WORLD_18 = 178, + SDLK_WORLD_19 = 179, + SDLK_WORLD_20 = 180, + SDLK_WORLD_21 = 181, + SDLK_WORLD_22 = 182, + SDLK_WORLD_23 = 183, + SDLK_WORLD_24 = 184, + SDLK_WORLD_25 = 185, + SDLK_WORLD_26 = 186, + SDLK_WORLD_27 = 187, + SDLK_WORLD_28 = 188, + SDLK_WORLD_29 = 189, + SDLK_WORLD_30 = 190, + SDLK_WORLD_31 = 191, + SDLK_WORLD_32 = 192, + SDLK_WORLD_33 = 193, + SDLK_WORLD_34 = 194, + SDLK_WORLD_35 = 195, + SDLK_WORLD_36 = 196, + SDLK_WORLD_37 = 197, + SDLK_WORLD_38 = 198, + SDLK_WORLD_39 = 199, + SDLK_WORLD_40 = 200, + SDLK_WORLD_41 = 201, + SDLK_WORLD_42 = 202, + SDLK_WORLD_43 = 203, + SDLK_WORLD_44 = 204, + SDLK_WORLD_45 = 205, + SDLK_WORLD_46 = 206, + SDLK_WORLD_47 = 207, + SDLK_WORLD_48 = 208, + SDLK_WORLD_49 = 209, + SDLK_WORLD_50 = 210, + SDLK_WORLD_51 = 211, + SDLK_WORLD_52 = 212, + SDLK_WORLD_53 = 213, + SDLK_WORLD_54 = 214, + SDLK_WORLD_55 = 215, + SDLK_WORLD_56 = 216, + SDLK_WORLD_57 = 217, + SDLK_WORLD_58 = 218, + SDLK_WORLD_59 = 219, + SDLK_WORLD_60 = 220, + SDLK_WORLD_61 = 221, + SDLK_WORLD_62 = 222, + SDLK_WORLD_63 = 223, + SDLK_WORLD_64 = 224, + SDLK_WORLD_65 = 225, + SDLK_WORLD_66 = 226, + SDLK_WORLD_67 = 227, + SDLK_WORLD_68 = 228, + SDLK_WORLD_69 = 229, + SDLK_WORLD_70 = 230, + SDLK_WORLD_71 = 231, + SDLK_WORLD_72 = 232, + SDLK_WORLD_73 = 233, + SDLK_WORLD_74 = 234, + SDLK_WORLD_75 = 235, + SDLK_WORLD_76 = 236, + SDLK_WORLD_77 = 237, + SDLK_WORLD_78 = 238, + SDLK_WORLD_79 = 239, + SDLK_WORLD_80 = 240, + SDLK_WORLD_81 = 241, + SDLK_WORLD_82 = 242, + SDLK_WORLD_83 = 243, + SDLK_WORLD_84 = 244, + SDLK_WORLD_85 = 245, + SDLK_WORLD_86 = 246, + SDLK_WORLD_87 = 247, + SDLK_WORLD_88 = 248, + SDLK_WORLD_89 = 249, + SDLK_WORLD_90 = 250, + SDLK_WORLD_91 = 251, + SDLK_WORLD_92 = 252, + SDLK_WORLD_93 = 253, + SDLK_WORLD_94 = 254, + SDLK_WORLD_95 = 255, /* 0xFF */ + + /* Numeric keypad */ + SDLK_KP0 = 256, + SDLK_KP1 = 257, + SDLK_KP2 = 258, + SDLK_KP3 = 259, + SDLK_KP4 = 260, + SDLK_KP5 = 261, + SDLK_KP6 = 262, + SDLK_KP7 = 263, + SDLK_KP8 = 264, + SDLK_KP9 = 265, + SDLK_KP_PERIOD = 266, + SDLK_KP_DIVIDE = 267, + SDLK_KP_MULTIPLY = 268, + SDLK_KP_MINUS = 269, + SDLK_KP_PLUS = 270, + SDLK_KP_ENTER = 271, + SDLK_KP_EQUALS = 272, + + /* Arrows + Home/End pad */ + SDLK_UP = 273, + SDLK_DOWN = 274, + SDLK_RIGHT = 275, + SDLK_LEFT = 276, + SDLK_INSERT = 277, + SDLK_HOME = 278, + SDLK_END = 279, + SDLK_PAGEUP = 280, + SDLK_PAGEDOWN = 281, + + /* Function keys */ + SDLK_F1 = 282, + SDLK_F2 = 283, + SDLK_F3 = 284, + SDLK_F4 = 285, + SDLK_F5 = 286, + SDLK_F6 = 287, + SDLK_F7 = 288, + SDLK_F8 = 289, + SDLK_F9 = 290, + SDLK_F10 = 291, + SDLK_F11 = 292, + SDLK_F12 = 293, + SDLK_F13 = 294, + SDLK_F14 = 295, + SDLK_F15 = 296, + + /* Key state modifier keys */ + SDLK_NUMLOCK = 300, + SDLK_CAPSLOCK = 301, + SDLK_SCROLLOCK = 302, + SDLK_RSHIFT = 303, + SDLK_LSHIFT = 304, + SDLK_RCTRL = 305, + SDLK_LCTRL = 306, + SDLK_RALT = 307, + SDLK_LALT = 308, + SDLK_RMETA = 309, + SDLK_LMETA = 310, + SDLK_LSUPER = 311, /* Left "Windows" key */ + SDLK_RSUPER = 312, /* Right "Windows" key */ + SDLK_MODE = 313, /* "Alt Gr" key */ + SDLK_COMPOSE = 314, /* Multi-key compose key */ + + /* Miscellaneous function keys */ + SDLK_HELP = 315, + SDLK_PRINT = 316, + SDLK_SYSREQ = 317, + SDLK_BREAK = 318, + SDLK_MENU = 319, + SDLK_POWER = 320, /* Power Macintosh power key */ + SDLK_EURO = 321, /* Some european keyboards */ + SDLK_UNDO = 322, /* Atari keyboard has Undo */ + + /* Add any other keys here */ + + + // ROBLOX Gamepad stuff + SDLK_GAMEPAD_BUTTONX = 1000, + SDLK_GAMEPAD_BUTTONY = 1001, + SDLK_GAMEPAD_BUTTONA = 1002, + SDLK_GAMEPAD_BUTTONB = 1003, + SDLK_GAMEPAD_BUTTONR1 = 1004, + SDLK_GAMEPAD_BUTTONL1 = 1005, + SDLK_GAMEPAD_BUTTONR2 = 1006, + SDLK_GAMEPAD_BUTTONL2 = 1007, + SDLK_GAMEPAD_BUTTONR3 = 1008, + SDLK_GAMEPAD_BUTTONL3 = 1009, + SDLK_GAMEPAD_BUTTONSTART = 1010, + SDLK_GAMEPAD_BUTTONSELECT = 1011, + SDLK_GAMEPAD_DPADLEFT = 1012, + SDLK_GAMEPAD_DPADRIGHT = 1013, + SDLK_GAMEPAD_DPADUP = 1014, + SDLK_GAMEPAD_DPADDOWN = 1015, + SDLK_GAMEPAD_THUMBSTICK1 = 1016, + SDLK_GAMEPAD_THUMBSTICK2 = 1017, + + SDLK_LAST + }; + enum ModCode { + KMOD_NONE = 0x0000, + KMOD_LSHIFT= 0x0001, + KMOD_RSHIFT= 0x0002, + KMOD_LCTRL = 0x0040, + KMOD_RCTRL = 0x0080, + KMOD_LALT = 0x0100, + KMOD_RALT = 0x0200, + KMOD_LMETA = 0x0400, + KMOD_RMETA = 0x0800, + KMOD_NUM = 0x1000, + KMOD_CAPS = 0x2000, + KMOD_MODE = 0x4000, + KMOD_RESERVED = 0x8000 + }; + +} // namespace \ No newline at end of file diff --git a/App/util/KeywordFilter.h b/App/util/KeywordFilter.h new file mode 100644 index 0000000..ea4d730 --- /dev/null +++ b/App/util/KeywordFilter.h @@ -0,0 +1,9 @@ +#pragma once + +namespace RBX { + + typedef enum KeywordFilterType { INCLUDE_KEYWORDS = 0, + EXCLUDE_KEYWORDS } KeywordFilterType; + + +} // namespace RBX \ No newline at end of file diff --git a/App/util/LRUCache.h b/App/util/LRUCache.h new file mode 100644 index 0000000..fc8100f --- /dev/null +++ b/App/util/LRUCache.h @@ -0,0 +1,267 @@ +#pragma once + +#include +#include +#include +#include "Util/StandardOut.h" + +namespace RBX +{ + +template +class LRUCache +{ +public: + + typedef std::list< std::pair< Key, std::pair > > List; ///< Main cache storage typedef + typedef typename List::iterator List_Iter; ///< Main cache iterator + typedef typename List::const_iterator List_cIter; ///< Main cache iterator (const) + + typedef boost::unordered_map Map; ///< Index typedef + + typedef typename Map::iterator Map_Iter; ///< Index iterator + typedef typename Map::const_iterator Map_cIter; ///< Index iterator (const) + +protected: + /// Main cache storage + List list; + /// Cache storage index + Map index; + + unsigned long totalMemory; + +public: + LRUCache() : totalMemory(0) {} + ~LRUCache() + { + this->clear(); + } + + inline void printContentNames() + { + for(List_Iter iter = list.begin(); iter != list.end(); ++iter) + { + StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "%s", iter->first.c_str()); + } + } + + inline unsigned long size() + { + return list.size(); + } + + inline unsigned long memSize() + { + return this->totalMemory; + } + + inline void clear() + { + list.clear(); + index.clear(); + totalMemory = 0; + } + + inline bool exists( const Key &key ) const + { + return index.find( key ) != index.end(); + } + inline bool empty() const + { + return list.empty(); + } + inline bool remove( const Key &key ) + { + Map_Iter miter = index.find( key ); + if( miter == index.end() ) return false; + remove( miter ); + return true; + } + /*inline void touch( const Key &key ) + { + internalTouch(key); + }*/ + + inline bool fetch( const Key &key, Data* result, unsigned long* size, bool touch = true ) + { + Map_Iter miter = index.find( key ); + if( miter == index.end() ) return false; + + if(touch){ + this->internalTouch( key ); + } + if(result){ + (*result) = miter->second->second.second; // map -> list -> pair -> value + } + if(size){ + (*size) = miter->second->second.first; + } + return true; + } + + inline bool fetch( const Key &key, Data* result, bool touch = true ) + { + unsigned long size = 0; + return fetch(key, result, &size, touch); + } + + inline virtual void resize( unsigned long newSize) + { + while( list.size() > newSize) { + // Remove the last element. + List_Iter liter = list.end(); + --liter; + this->remove( liter->first ); + } + } + + // returns the size of last removed element + inline void removeLeastRecentlyUsed() + { + // Remove the last element. + List_Iter liter = list.end(); + --liter; + this->remove( liter->first ); + } + + inline virtual void insert( const Key &key, const Data &data, const unsigned long dataSize = 0) + { + // Touch the key, if it exists, then replace the content. + Map_Iter miter = this->internalTouch( key ); + if( miter != index.end() ) + this->remove( miter ); + + // Ok, do the actual insert at the head of the list + list.push_front( std::make_pair( key, std::make_pair(dataSize, data) ) ); + List_Iter liter = list.begin(); + + // Store the index + index.insert( std::make_pair( key, liter ) ); + + totalMemory += dataSize; + } + + inline void insert(List_cIter iter, List_cIter iterEnd) + { + for(; iter != iterEnd; ++iter){ + insert(iter->first, iter->second.second, iter->second.first); + } + } + + inline List_Iter begin() + { + return list.begin(); + } + inline List_Iter end() + { + return list.end(); + } +private: + inline Map_Iter internalTouch( const Key &key ) + { + Map_Iter miter = index.find( key ); + if( miter == index.end() ) return miter; + // Move the found node to the head of the list. + list.splice( list.begin(), list, miter->second ); + return miter; + } + inline void remove( const Map_Iter &miter ) + { + totalMemory -= miter->second->second.first; + list.erase( miter->second ); + index.erase( miter ); + } +}; + +template +class SizeEnforcedLRUCache : public LRUCache +{ + typedef LRUCache Super; + + /// Maximum size of the cache in elements + unsigned long maxSize; + +public: + + SizeEnforcedLRUCache(const unsigned long maxSize) + : maxSize(maxSize) + {} + ~SizeEnforcedLRUCache() {} + + inline void resize( unsigned long newSize) + { + maxSize = newSize; + Super::resize(maxSize); + } + + inline void insert( const Key &key, const Data &data, const unsigned long dataSize = 0) + { + Super::insert(key, data, dataSize); + + // Check to see if we need to remove an element due to exceeding max_size + if( this->list.size() > maxSize ) { + Super::removeLeastRecentlyUsed(); + } + } +}; + +template +class MemEnforcedLRUCache : public LRUCache +{ + typedef LRUCache Super; + + // Maximum memory size of all the elements in the cache + unsigned long maxMemSize; + +public: + + MemEnforcedLRUCache (const unsigned long maxSize) : maxMemSize(maxSize) {} + + inline virtual void resize( unsigned long newSize) + { + maxMemSize = newSize; + while( this->totalMemory > maxMemSize ) + { + Super::removeLeastRecentlyUsed(); + RBXASSERT(this->totalMemory >= 0); + } + } + + inline void insert( const Key &key, const Data &data, const unsigned long dataSize) + { + Super::insert(key, data, dataSize); + + // Check to see if we need to remove an element due to exceeding max_size + while( this->totalMemory > maxMemSize ) { + Super::removeLeastRecentlyUsed(); + RBXASSERT(this->totalMemory >= 0); + } + } +}; + +template +class ConcurrentLRUCache +{ +public: + RBX::LRUCache cache; + boost::mutex mutex; + public: + ConcurrentLRUCache (int size) + : cache(size) + {} + + bool fetch(const Key& id, Data* result) + { + boost::mutex::scoped_lock lock(mutex); + return cache.fetch(id, result); + } + + void insert(const Key& id, const Data& data) + { + boost::mutex::scoped_lock lock(mutex); + cache.insert(id, data); + } +}; + + +} \ No newline at end of file diff --git a/App/util/Lcmrand.h b/App/util/Lcmrand.h new file mode 100644 index 0000000..b8a9bc9 --- /dev/null +++ b/App/util/Lcmrand.h @@ -0,0 +1,24 @@ +/* Copyright 2014 ROBLOX Corporation, All Rights Reserved */ +#include "stdafx.h" + +class LcmRand +{ +public: + LcmRand() : seed(1337U) {} + + uint32_t value() + { + const static uint32_t a = 214013U; + const static uint32_t c = 2531011U; + seed = seed * a + c; + return (seed >> 16) & 0x7FFF; + } + + void setSeed(uint32_t newSeed) + { + seed = newSeed; + } + +private: + uint32_t seed; +}; \ No newline at end of file diff --git a/App/util/LegacyContentTable.h b/App/util/LegacyContentTable.h new file mode 100644 index 0000000..2239461 --- /dev/null +++ b/App/util/LegacyContentTable.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace RBX { + + class LegacyContentTable + { + private: + typedef boost::unordered_map UrlMap; + UrlMap mMap; + std::string mEmpty; + + public: + LegacyContentTable(); + + void AddEntry(const std::string& path, const std::string& contentId); + void AddEntryProd(const std::string& path, const std::string& contentId); + const std::string& FindEntry(const std::string& path); + }; +} \ No newline at end of file diff --git a/App/util/LuaWebService.h b/App/util/LuaWebService.h new file mode 100644 index 0000000..e0ba580 --- /dev/null +++ b/App/util/LuaWebService.h @@ -0,0 +1,140 @@ +#pragma once +#include "V8Tree/Service.h" +#include "Util/AsyncHttpCache.h" + +namespace RBX { + extern const char* const sPages; + + class Pages : + public DescribedNonCreatable + { + protected: + bool finished; + shared_ptr currentPage; + + public: + Pages(); + + virtual void fetchNextChunk(boost::function resumeFunction, boost::function errorFunction) {}; + + shared_ptr getCurrentPage(); + bool isFinished() const; + void advanceToNextPageAsync(boost::function resumeFunction, boost::function errorFunction); + }; + + extern const char* const sStandardPages; + + class StandardPages : + public DescribedNonCreatable + { + weak_ptr weakDM; + std::string fieldName; + std::string requestUrl; + int pageNumber; + + void processFetchSuccess(std::string response, boost::function resumeFunction, boost::function errorFunction); + void processFetchError(std::string error, boost::function errorFunction); + void processFetch(std::string* response, std::exception* exception, boost::function resumeFunction, boost::function errorFunction); + + public: + StandardPages(weak_ptr weakDM, const std::string& requestUrl, const std::string& fieldName); + virtual void fetchNextChunk(boost::function resumeFunction, boost::function errorFunction); + }; + + extern const char* const sFriendPages; + + class FriendPages : + public DescribedNonCreatable + { + weak_ptr weakDM; + std::string fieldName; + std::string requestUrl; + shared_ptr nextPage; + int pageNumber; + bool firstTime; + + void processFetchSuccess(std::string response, boost::function resumeFunction, boost::function errorFunction); + void processFetchError(std::string error, boost::function errorFunction); + void processFetch(std::string* response, std::exception* exception, boost::function resumeFunction, boost::function errorFunction); + + public: + FriendPages(weak_ptr weakDM, const std::string& requestUrl); + virtual void fetchNextChunk(boost::function resumeFunction, boost::function errorFunction); + }; + + extern const char *const sLuaWebService; + + #define LUA_WEB_SERVICE_STANDARD_PRIORITY 50 + + // This service is used to make web queries. It is frequently used by + // function calls originating in Lua, but is not restricted to use + // by Lua + class LuaWebService + : public DescribedNonCreatable + , public Service + { + private: + typedef DescribedNonCreatable Super; + + struct CachedLuaWebServiceInfo + { + Reflection::Variant value; + CachedLuaWebServiceInfo() {} + CachedLuaWebServiceInfo(shared_ptr data, shared_ptr filename); + }; + + struct CachedRawLuaWebServiceInfo + { + std::string value; + CachedRawLuaWebServiceInfo() {} + CachedRawLuaWebServiceInfo(shared_ptr data, shared_ptr filename); + }; + + boost::shared_ptr > webCache; + boost::shared_ptr > webRawCache; + bool checkApiAccess; + Time timeToRecheckApiAccess; + boost::optional apiAccess; + + template + bool checkCache(const std::string& url, boost::function resumeFunction, boost::function errorFunction); + + template + static bool TryDispatchRequest(AsyncHttpCache* webCache, const std::string& url, + boost::function resumeFunction, boost::function errorFunction); + + template + static bool TryRawDispatchRequest(AsyncHttpCache* webCache, const std::string& url, + boost::function resumeFunction, boost::function errorFunction); + + template + static void Callback(boost::weak_ptr weakLuaWebService, AsyncHttpQueue::RequestResult requestResult, std::string url, + boost::function resumeFunction, boost::function errorFunction); + + static void RawCallback(boost::weak_ptr weakLuaWebService, AsyncHttpQueue::RequestResult requestResult, std::string url, + boost::function resumeFunction, boost::function errorFunction); + public: + LuaWebService(); + void asyncRequest(const std::string& url, float priority, + boost::function)> resumeFunction, boost::function errorFunction); + void asyncRequest(const std::string& url, float priority, + boost::function)> resumeFunction, boost::function errorFunction); + void asyncRequest(const std::string& url, float priority, + boost::function resumeFunction, boost::function errorFunction); + void asyncRequest(const std::string& url, float priority, + boost::function resumeFunction, boost::function errorFunction); + void asyncRequest(const std::string& url, float priority, + boost::function resumeFunction, boost::function errorFunction); + + //Skips the caches + void asyncRequestNoCache(const std::string& url, float priority, boost::function)> callback, AsyncHttpQueue::ResultJob resultJob); + + // will block until api access request has returned + bool isApiAccessEnabled(); + void setCheckApiAccessBecauseInStudio(); + + static bool parseWebJSONResponseHelper(std::string* response, std::exception* exception, + shared_ptr& result, std::string& status); + }; +} + diff --git a/App/util/MD5Hasher.h b/App/util/MD5Hasher.h new file mode 100644 index 0000000..b54dca6 --- /dev/null +++ b/App/util/MD5Hasher.h @@ -0,0 +1,29 @@ +#pragma once + +#include "rbx/declarations.h" +#include +#include + +namespace RBX { + + class RBXInterface MD5Hasher + { + public: + static MD5Hasher* create(); + virtual ~MD5Hasher() {} + virtual void addData(std::istream& data) = 0; + virtual void addData(const std::string& data) = 0; + virtual void addData(const char* data, size_t nBytes) = 0; + virtual const std::string& toString() = 0; + virtual const char* c_str() = 0; + + virtual void toBuffer(char (&result)[16]) = 0; + + // Before 03-12-07 the hashing function didn't pad bytes with '0' + static std::string convertToLegacyHash(std::string hash); + }; + + std::string CollectMd5Hash(const std::string& fileName); + std::string ComputeMd5Hash(const std::string& data); + +}//namespace \ No newline at end of file diff --git a/App/util/MachOBaseAddr.h b/App/util/MachOBaseAddr.h new file mode 100644 index 0000000..a0965c6 --- /dev/null +++ b/App/util/MachOBaseAddr.h @@ -0,0 +1,16 @@ +// +// MachOBaseAddr.h +// App +// +// Created by David Stahl on 11/10/14. +// +// + +#ifndef App_MachOBaseAddr_h +#define App_MachOBaseAddr_h + +uint32_t machODynamicBaseAddress(void); + +uint32_t machOTextSize(void); + +#endif diff --git a/App/util/MachineIdUploader.h b/App/util/MachineIdUploader.h new file mode 100644 index 0000000..baa0092 --- /dev/null +++ b/App/util/MachineIdUploader.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +namespace RBX { + +// Helper class to gather identifying information about this machine and for +// communicating with the web banned machine database. +class MachineIdUploader { +public: + static const char* kBannedMachineMessage; + enum Result { + RESULT_MachineAccepted = 1, + RESULT_MachineBanned = 0 + }; + // Gather identifying info for this machine, send it out, and return + // weather this machine has been banned or not. + static Result uploadMachineId(const char* baseUrl); + + static std::string getMachineId(); + +private: + struct MacAddress { + static const int kBytesInMacAddress = 6; + unsigned char address[kBytesInMacAddress]; + std::string asString() const; + }; + + struct MachineId { + std::vector macAddresses; + }; + + static bool fillMachineId(MachineId* out); + static bool buildMacAddressContent(bool needsLeadingAmp, const MachineId& id, std::stringstream& stream); + static void buildContent(const MachineId& id, std::stringstream& stream); +}; + +} diff --git a/App/util/Math.h b/App/util/Math.h new file mode 100644 index 0000000..355d801 --- /dev/null +++ b/App/util/Math.h @@ -0,0 +1,387 @@ +/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/NormalId.h" +#include "Util/G3DCore.h" +#include "Util/PV.h" +#include "rbx/Debug.h" +#include "G3D/Array.h" +#include "RbxG3D/RbxRay.h" +#include + +namespace RBX { + + inline int fastFloorInt(float value) + { + return value < 0 ? static_cast(value - 0.999f) : static_cast(value); + } + + inline int fastCeilInt(float value) + { + return value < 0 ? static_cast(value) : static_cast(value + 0.999f); + } + + inline Vector3int16 fastFloorInt16(const Vector3& v) + { + return Vector3int16(fastFloorInt(v.x), fastFloorInt(v.y), fastFloorInt(v.z)); + } + + typedef enum { + AXIS_X = 0, + AXIS_Y = 1, + AXIS_Z = 2 + } AxisIndex; + + namespace Math + { + inline double pi() {return 3.14159265358979323846;} + inline double piHalf() {return pi() * 0.5f;} + inline double twoPi() {return pi() * 2.0f;} + inline float pif() {return static_cast(pi());} + inline float piHalff() {return static_cast(piHalf());} + inline float twoPif() {return static_cast(twoPi());} + inline const float& inf() { + static const float i = std::numeric_limits::infinity(); + return i; + } + + // Returns the 0-based most-significant bit (-1 if v is 0) + inline size_t computeMSB(size_t v) + { + size_t msb = -1; + while (v>0) + { + v >>= 1; + ++msb; + } + return msb; + } + + inline int iRound(float value) { + return G3D::iRound(value); + } + inline int iFloor(float value) { + return G3D::iRound(::floor(value)); + } + inline float polarity(float value) { + return (value >= 0.0f) ? 1.0f : -1.0f; + } + inline float sign(float value) { + return (value > 0.0f) + ? 1.0f + : (value < 0.0f ? -1.0f : 0.0f); + } + + //////////////////////////////////////////////////////// + // + // Denormalized detection + + bool isDenormal(float f); + bool isNan(float f); + bool isNan(const Vector3& v); + bool isNanInf(float f); + bool isNanInfDenorm(float f); + bool isNanInfVector3(const Vector3& v); + bool isNanInfDenormVector3(const Vector3& v); + bool isNanInfDenormMatrix3(const Matrix3& m); + bool hasNanOrInf(const CoordinateFrame& c); + bool hasNanOrInf(const Matrix3& m); + + // Sets denormalized values to 0.0 + bool fixDenorm(float& f); + bool fixDenorm(Vector3& v); + + //////////////////////////////////////////////////////// + // + // fuzzyEq stuff + inline float epsilonf() { return 1.0e-6f; } + inline bool fuzzyEq(float a, float b, float epsilon) { + float aa = fabsf(a) + 1.0f; + return (a == b) || (fabsf(a - b) <= (aa * epsilon)); + } + + inline bool fuzzyEq(double a, double b, double epsilon) { + double aa = fabs(a) + 1.0f; + return (a == b) || (fabs(a - b) <= (aa * epsilon)); + } + + bool fuzzyEq(const Vector3& v0, const Vector3& v1, float epsilon = 1.0e-5f); // Note G3D::eps == 1e-6; + bool fuzzyEq(const Matrix3& m0, const Matrix3& m1, float epsilon = 1.0e-5f); // Note G3D::eps == 1e-6; + bool fuzzyEq(const Matrix4& m0, const Matrix4& m1, float epsilon = 1.0e-5f); // Note G3D::eps == 1e-6; + bool fuzzyEq(const CoordinateFrame& c0, const CoordinateFrame& c1, float epsT = 1.0e-5f, float epsRad = 1.0e-5f); + bool fuzzyAxisAligned(const Matrix3& m0, const Matrix3& m1, float radTolerance); + + //////////////////////////////////////////////////////// + // + // odd / even stuff + inline bool isEven(int value) { + return ((value % 2) == 0); + } + + inline bool isOdd(int value) { + return ((value % 2) != 0); + } + + inline int nextEven(int value) { + return (value + 1 + ((value + 1) % 2)); + } + + inline int nextOdd(int value) { + return (value + 1 + (value % 2)); + } + + //////////////////////////////////////////////////////// + // + // Vector2 stuff + inline Vector2 expandVector2(const Vector2& v, int expand) { + Vector2 answer(v); + for (int i = 0; i < 2; ++i) { + answer[i] += expand * RBX::Math::sign(v[i]); + } + return answer; + } + + inline Vector2 roundVector2(const Vector2& v) { + return Vector2(iRound(v.x), iRound(v.y)); + } + + //////////////////////////////////////////////////////// + // + // Vector3 stuff + + size_t hash(const Vector3& v); + bool isIntegerVector3(const Vector3& v); + Vector3 iRoundVector3(const Vector3& point); + float angle(const Vector3& v0, const Vector3& v1); + float smallAngle(const Vector3& v0, const Vector3& v1); + float elevationAngle(const Vector3& look); + Vector3 vector3Abs(const Vector3& v); + float volume(const Vector3& v); + float maxAxisLength(const Vector3& v); + Vector3 sortVector3(const Vector3& v); + Vector3 safeDirection(const Vector3& v); // handles case where V == vector3::zero(); + Velocity calcTrajectory(const Vector3& launch, const Vector3& target, float speed); + Vector3 toGrid(const Vector3& v, const Vector3& grid); + Vector3 toGrid(const Vector3& v, float grid); + bool lessThan(const Vector3& min, const Vector3& max); + inline float longestVector3Component(const Vector3& v) { + return std::max(fabs(v.x), std::max(fabs(v.y), fabs(v.z))); + } + inline float planarSize(const Vector3& v) { + return (v.x < v.y) + ? ((v.x < v.z) ? v.y * v.z : v.y * v.x) + : ((v.y < v.z) ? v.x * v.z : v.x * v.y); + } + inline float taxiCabMagnitude(const Vector3& v) { + return fabs(v.x) + fabs(v.y) + fabs(v.z); + } + float sumDeltaAxis(const Matrix3& r0, const Matrix3& r1); + + inline const Plane& yPlane() {static Plane p(Vector3(0.0, 1.0, 0.0), Vector3::zero()); return p;} + Vector3 closestPointOnRay(const RBX::RbxRay& pointOnRay, const RBX::RbxRay& otherRay); + + + //////////////////////////////////////////////////////// + // + // Manipulate/rotate Matrix3 and CoordinateFrame + + Vector3 rotateAboutYGlobal(const Vector3& v, float radians); + Vector3 toSmallAngles(const Matrix3& matrix); + Matrix3 snapToAxes(const Matrix3& matrix); + bool isOrthonormal(const Matrix3& m); + bool orthonormalizeIfNecessary(Matrix3& m); // true if an orthonormalize was necessary + + Vector3 toFocusSpace(const Vector3& goal, const CoordinateFrame& focus); + Vector3 fromFocusSpace(const Vector3& goal, const CoordinateFrame& focus); + + Vector3 toDiagonal(const Matrix3& m); + + inline Matrix3 fromDiagonal(const Vector3& v) { + return Matrix3( v[0], 0.0f, 0.0f, + 0.0f, v[1], 0.0f, + 0.0f, 0.0f, v[2] ); + } + + // Return the skew symmetric matrix for the the given vector. + // a.cross( b ) = A_* b where A_is the skew symmetric matrix for a. + // + inline Matrix3 toSkewSymmetric(const Vector3& v) { + return Matrix3( 0.0f, -v.z, v.y, + v.z, 0.0f, -v.x, + -v.y, v.x, 0.0f ); + } + + Matrix3 fromVectorToVectorRotation( const Vector3& fromVec, const Vector3& toVec ); + Matrix3 fromRotationAxisAndAngle( const Vector3& axis, const float& angleRads ); + Matrix3 fromShortestPlanarRotation( const Vector3& targetX, const Vector3& targetY ); + Matrix3 fromDirectionCosines( const Vector3& fromX, const Vector3& fromY, const Vector3& fromZ, + const Vector3& toX, const Vector3& toY, const Vector3& toZ ); + + inline Vector3 getColumn(const Matrix3& m, int iCol) { + RBXASSERT_VERY_FAST((0 <= iCol) && (iCol < 3)); + return Vector3(m[0][iCol], m[1][iCol], m[2][iCol]); + } + + void mulMatrixDiagVector(const Matrix3& _mat, const Vector3& _vec, Matrix3& _answer); + void mulMatrixMatrixTranspose(const Matrix3& _m0, const Matrix3& _m1, Matrix3& _answer); + void mulMatrixTransposeMatrix(const Matrix3& _m0, const Matrix3& _m1, Matrix3& _answer); + + // Byte Angles + unsigned char rotationToByte(float angle); + float rotationFromByte(unsigned char byteAngle); + + // Axis Aligned Matrix / OrientId + static const int maxOrientationId = 36; + static const int minOrientationId = 0; + bool isAxisAligned(const Matrix3& matrix); + int getOrientId(const Matrix3& matrix); + void idToMatrix3(int orientId, Matrix3& matrix); + + const Matrix3& matrixRotateX(); + //static const Matrix3& matrixRotateNegativeX(); + const Matrix3& matrixRotateY(); + const Matrix3& matrixRotateNegativeY(); + const Matrix3& matrixTiltZ(); + const Matrix3& matrixTiltNegativeZ(); + const Matrix3 matrixTiltQuadrant(int quadrant); + void rotateMatrixAboutX90(Matrix3& matrix, int times = 1); + void rotateMatrixAboutY90(Matrix3& matrix, int times = 1); + void rotateMatrixAboutZ90(Matrix3& matrix); + Matrix3 rotateAboutZ(const Matrix3& matrix, float radians); + Matrix3 getWellFormedRotForZVector(const Vector3& vec); + Matrix3 momentToObjectSpace(const Matrix3& iWorld, const Matrix3& bodyRotation); + Matrix3 momentToWorldSpace(const Matrix3& iBody, const Matrix3& bodyRotation); + Matrix3 getIWorldAtPoint(const Vector3& cofmPos, + const Vector3& worldPos, + const Matrix3& iWorldAtCofm, + float mass); + Matrix3 getIBodyAtPoint(const Vector3& pos, + const Matrix3& iBody, + float mass); + + // CoordinateFrame + void rotateAboutYLocal(CoordinateFrame& c, float radians); + void rotateAboutYGlobal(CoordinateFrame& c, float radians); + CoordinateFrame snapToGrid(const CoordinateFrame& snap, float grid); + CoordinateFrame snapToGrid(const CoordinateFrame& snap, const Vector3& grid); + // http://www.vlfeat.org/api/mathop_8h-source.html#l00227 + inline float atan2Fast(float y, float x) { + float angle, r; + float const c3 = 0.1821f; + float const c1 = 0.9675f; + float abs_y = fabsf(y) + 1.19209290e-07f; + if (x >= 0) { + r = (x - abs_y) / (x + abs_y) ; + angle = Math::pif() / 4.0f; + } else { + r = (x + abs_y) / (abs_y - x) ; + angle = 3.0f * Math::pif() / 4.0f ; + } + angle += (c3*r*r - c1) * r ; + return (y < 0) ? - angle : angle ; + } + + inline float zAxisAngle(const Matrix3& matrix) { + Vector3 look = matrix.column(0); + float angle = (float) atan2(look.y, look.x); +// float angle = Math::atan2Fast(look.y, look.x); + return angle; + } + void pan(const Vector3& focusPosition, CoordinateFrame& camera, float radians); + + // std::vector + void lerpArray( + const G3D::Array& before, + const G3D::Array& after, + G3D::Array& answer, + float alpha); + + // Pitch, Yaw stuff - replaces Euler Angles + int radiansToQuadrant(float radians); + int radiansToOctant(float radians); + inline float radiansToDegrees(float radians) {return radians * (180.0f / Math::pif());} + inline float degreesToRadians(float degrees) {return degrees * (Math::pif() / 180.0f);} + + /** + Returns the heading as an angle in radians, where + north is 0 and west is PI/2 + North == -z + + Elevation is angle above (+) or below(-) horizon + */ + inline float getHeading(const Vector3& look) { return atan2( -look.x, -look.z); } + inline float getElevation(const Vector3& look) { return asin(look.y); } + + void getHeadingElevation(const CoordinateFrame& c, float& heading, float& elevation); + void setHeadingElevation(CoordinateFrame& c, float heading, float elevation); + + CoordinateFrame getFocusSpace(const CoordinateFrame& focus); + + int toYAxisQuadrant(const CoordinateFrame& c); // 0..3 + + Matrix3 alignAxesClosest(const Matrix3& align, const Matrix3& target); + + // NormalId stuff + NormalId getClosestObjectNormalId(const Vector3& worldV, const Matrix3& objectR); + + inline Vector3 getWorldNormal(NormalId objId, const Matrix3& objectR) { +// return (objId < 3) ? getColumn(objectR, objId) : -getColumn(objectR, objId - 3); + int column = objId % 3; + int polarity = ((objId / 3) * (-2)) + 1; + return polarity * getColumn(objectR, column); + } + + inline Vector3 getWorldNormal(NormalId objId, const CoordinateFrame& objectC) { + return getWorldNormal(objId, objectC.rotation); + } + + // wraps from -pi to pi + + float deltaRotationClose(float aRot, float bRot); // computes aRot - bRot, assuming angles are close. Undoes wrapping + float averageRotationClose(float aRot, float bRot); // computes average aRot, bRot, assuming angles are close. Undoes wrapping + + double advanceWoundRotation(double currentRotationWound, double newRotationNotWound); // properly increments a wound up rotation - detects flips + float clampRotationClose(float rot, float limitLo, float limitHi); + // -3pi to -pi: -1 + // -pi to pi: 0 + // pi to 3pi: 1 + inline double windingPart(double rad) { + return ::floor((rad + pi()) / twoPi()) ; + } + + inline float radWrap(double rad) { // extra part + if ((rad >= -pi()) && (rad < pi())) { + return static_cast(rad); + } + double answer = rad - (twoPi() * windingPart(rad)); + RBXASSERT((answer >= -pi()) && (answer <= pi())); + return static_cast(answer); + } + + // Matrix operations + const Matrix3& getAxisRotationMatrix(int face); + + // Vector to Object Space + // == mat.transpose() * vec + inline Vector3 vectorToObjectSpace(const Vector3& vec, const Matrix3& mat); + + // Ray, Line + bool clipRay(Vector3& origin, Vector3& ray, Vector3 box[], Vector3& endPoint); + bool intersectLinePlane(const Line& line, const Plane& plane, Vector3& hit); + bool intersectRayPlane(const RbxRay& ray, const Plane& plane, Vector3& hit); + bool intersectRayConvexPolygon(const RBX::RbxRay& ray, const std::vector& poly, Vector3& hit, bool oneSided); + bool lineSegmentDistanceIfCrossing(const Vector3& line1Pt1, const Vector3& line1Pt2, const Vector3& line2Pt1, const Vector3& line2Pt2, float& distance, float adjustEdgeTol = 0.0f); + std::vector spatialPolygonIntersection(const std::vector& polyA, const std::vector& polyB); + std::vector planarPolygonIntersection(const std::vector& poly1, const std::vector& poly2); + + + // Misc. + float computeLaunchAngle(float v, float x, float y, float g); + Vector2 polygonStartingPoint(int numSides, float maxWidth); + bool evenWholeNumber( const float& rawInput ); + bool evenWholeNumberFuzzy( const float& rawInput ); + } +} // namespace + +#include "Math.inl" diff --git a/App/util/Math.inl b/App/util/Math.inl new file mode 100644 index 0000000..df4e21a --- /dev/null +++ b/App/util/Math.inl @@ -0,0 +1,18 @@ +#pragma once + +namespace RBX { + namespace Math{ + +// = mat.transpose() * vec +Vector3 vectorToObjectSpace(const Vector3& _vec, const Matrix3& _mat) +{ + const float* vec = &_vec[0]; + const float* mat = &_mat[0][0]; + + return Vector3 ( mat[0]*vec[0] + mat[3]*vec[1] + mat[6]*vec[2], + mat[1]*vec[0] + mat[4]*vec[1] + mat[7]*vec[2], + mat[2]*vec[0] + mat[5]*vec[1] + mat[8]*vec[2] ); +} + + } // namespace +} // namespace diff --git a/App/util/Memory.h b/App/util/Memory.h new file mode 100644 index 0000000..e49b3c2 --- /dev/null +++ b/App/util/Memory.h @@ -0,0 +1,2 @@ + +#include "rbx/memory.h" \ No newline at end of file diff --git a/App/util/MemoryStats.h b/App/util/MemoryStats.h new file mode 100644 index 0000000..746689a --- /dev/null +++ b/App/util/MemoryStats.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include "standardout.h" +#include "FastLog.h" + +namespace RBX { + +// Utility functions for determining used/free/total memory. +namespace MemoryStats { + +enum MemoryLevel +{ + MEMORYLEVEL_ALL_CRITICAL_LOW, + MEMORYLEVEL_ONLY_PHYSICAL_CRITICAL_LOW, + MEMORYLEVEL_ALL_LOW, + MEMORYLEVEL_ONLY_PHYSICAL_LOW, + MEMORYLEVEL_LIMITED, + MEMORYLEVEL_OK +}; + +typedef boost::uint64_t memsize_t; + +memsize_t usedMemoryBytes(); +memsize_t freeMemoryBytes(); +memsize_t totalMemoryBytes(); +size_t slowGetMemoryPoolAllocation(); +size_t slowGetMemoryPoolAvailability(); +void releaseAllPoolMemory(); +MemoryLevel slowCheckMemoryLevel(memsize_t extraMemoryUsed); + +} // namespace MemoryStats +} // namespace RBX diff --git a/App/util/MeshId.h b/App/util/MeshId.h new file mode 100644 index 0000000..e82ad8c --- /dev/null +++ b/App/util/MeshId.h @@ -0,0 +1,22 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Tree/Service.h" +#include "Util/RunStateOwner.h" +#include "Reflection/Event.h" + + +namespace RBX { + + class PartInstance; + + class MeshId : public ContentId + { + public: + MeshId(const ContentId& id):ContentId(id) {} + MeshId(const char* id):ContentId(id) {} + MeshId(const std::string& id):ContentId(id) {} + MeshId() {} + }; +} // namespace RBX diff --git a/App/util/MovementHistory.h b/App/util/MovementHistory.h new file mode 100644 index 0000000..1425654 --- /dev/null +++ b/App/util/MovementHistory.h @@ -0,0 +1,123 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ +#pragma once + +#include +#include "G3DCore.h" +#include "rbx/rbxTime.h" +#include "boost/thread/mutex.hpp" +#include "Math.h" + +namespace RBX { + +#define MH_NUM_MAX_NODES 80 +#define MH_MIN_PRECISION 0.01f +#define MH_TOLERABLE_COMPRESSION_ERROR 1.f + + + typedef unsigned char uint8_t; + typedef signed char int8_t; + + class MovementHistory + { + + public: + struct DeltaCompressedTranslation + { + // in terms of MIN_PRECISION + uint8_t precisionLevel; + int8_t dX; + int8_t dY; + int8_t dZ; + }; + + struct MovementNode + { + DeltaCompressedTranslation translation; + uint8_t delta2Ms; + MovementNode() + { + setZero(); + } + MovementNode(const CoordinateFrame& newCFrame, const CoordinateFrame& oldCFrame, float deltaSecs) + { + Vector3 delta = newCFrame.translation - oldCFrame.translation; + compress(delta, *this); + if (deltaSecs < 0.f) + { + delta2Ms = 0; + } + else if (deltaSecs > 0.510f) + { + delta2Ms = 255; // overflow, use 255 to indicate the long gap + } + else + { + delta2Ms = (uint8_t)(deltaSecs*500.f); + } + } + bool isZero() const + { + return translation.dX == 0 && translation.dY == 0 && translation.dZ == 0; + } + void setZero() + { + translation.precisionLevel = 0; + translation.dX = 0; + translation.dY = 0; + translation.dZ = 0; + delta2Ms = 0; + } + // rotation will be estimated by interpolation + }; + + static const MovementHistory& getDefaultHistory() + { + static CoordinateFrame zeroCFrame; + static Velocity zeroVelocity; + static MovementHistory defaultMovementHistory(zeroCFrame, zeroVelocity, Time()); + return defaultMovementHistory; + } + + MovementHistory(const CoordinateFrame& cFrame, const Velocity& velocity, const Time& timeStamp); + ~MovementHistory() + {} + + void clearNodeHistory(); + void addNode(const CoordinateFrame& cFrame, const Velocity& velocity, const Time& timeStamp); + + size_t getNumNodes() const {return size;} + bool hasHistory(float accumulatedError) const; + void getMovementNodeList(const Time& lastCutOffTime, const Time& currentCutOffTime, std::deque& result, bool crossPacketCompression, const CoordinateFrame& lastSentCFrame, CoordinateFrame& outCalculatedBaselineCFrame, Vector3& outCalculatedLinearVelocity) const; + const CoordinateFrame& getBaselineCFrame() const {return baselineCFrame;} + const Velocity& getBaselineVelocity() const {return baselineVelocity;} + + static float decompress(int8_t v, uint8_t precisionLevel); + static void decompress(MovementNode node, Vector3& outTranslation); + + const Time& getLastUpdateTime() const {return lastUpdateTime;} + float getTimeSpan() const {return timeSpanSec;} + + static float getSecFrom2Ms(uint8_t delta2MS) + { + return ((float)delta2MS)/500.f; + } + + private: + CoordinateFrame baselineCFrame; + Velocity baselineVelocity; + Time lastUpdateTime; + float timeSpanSec; + int checksum; + + MovementNode movementNodes[MH_NUM_MAX_NODES]; + size_t startIndex; + size_t size; + void popFront(); + void pushBack(MovementNode node); + MovementNode concatNode(size_t lastIndex, size_t numNodesToConcat) const; + + static int8_t compress(float v, uint8_t precisionLevel); + static void compress(Vector3 translation, MovementNode& outMovementNode); + }; + +} // namespace diff --git a/App/util/Name.h b/App/util/Name.h new file mode 100644 index 0000000..ebd0eed --- /dev/null +++ b/App/util/Name.h @@ -0,0 +1,168 @@ + + +#ifndef _E34E3E6DF0724eb493E138F10DF08D03 +#define _E34E3E6DF0724eb493E138F10DF08D03 + +#include +#include +#include "boost/utility.hpp" + +#include "rbx/Debug.h" +#include "rbx/boost.hpp" +#include "rbx/atomic.h" +#include +#include "rbx/threadsafe.h" + +#include "security/ApiSecurity.h" + +namespace RBX { + + class Name : public boost::noncopyable + { + static RBX::mutex& mutex(); + class NameMap; + static NameMap& map(); + + template + static const Name& doDeclare() + { + static const Name& n = declare(sName); + return n; + } + template + static void callDoDeclare() + { + doDeclare(); + } + + // sortIndex is atomic to avoid any chance of stale + // data when calling setOrderIndex + rbx::atomic sortIndex; + + public: + std::string const str; // the string that is the text name + + static size_t size(); + static size_t approximateMemoryUsage(); + + // Declaration and Query + static const Name& getNullName(); + + // Fast and thread-safe + NOINLINE static const Name& declare(const char* const& sName); + FORCEINLINE static const Name& declare(const std::string& sName) + { + return declare(sName.c_str()); + } + template + static const Name& declare() + { + if(sName == NULL) + return getNullName(); + + static boost::once_flag flag = BOOST_ONCE_INIT; + boost::call_once(&callDoDeclare, flag); + return doDeclare(); + } + + NOINLINE static const Name& lookup(const char* const& sName); + FORCEINLINE static const Name& lookup(const std::string& sName) + { + return lookup(sName.c_str()); + } + + bool empty() const { return getNullName()==*this; } + static bool empty(const Name* name) { return name==0 || *name==getNullName(); } + + // Convert to string + const std::string& toString() const { return str; } + const char* c_str() const { return str.c_str(); } + + // Comparison +#if 1 + // Optimization - avoids string comparisons + static inline int compare(const Name& a, const Name& b) { + return a.sortIndex - b.sortIndex; + } + inline int compare(const Name& other) const { + return sortIndex - other.sortIndex; + } + inline bool operator < (const Name& other) const { + return sortIndex < other.sortIndex; + } + inline bool operator > (const Name& other) const { + return sortIndex > other.sortIndex; + } +#else + static inline int compare(const Name& a, const Name& b) { + return a.str.compare(b.str); + } + inline int compare(const Name& other) const { + return str.compare(other.str); + } + inline bool operator < (const Name& other) const { + return str < other.str; + } + inline bool operator > (const Name& other) const { + return str > other.str; + } +#endif + inline bool operator == (const Name& other) const { + return this==&other; + } + inline bool operator != (const Name& other) const { + return this!=&other; + } + inline bool operator == (const std::string& sName) const { + return this->str == sName; + } + inline bool operator != (const std::string& sName) const { + return this->str != sName; + } + inline bool operator == (const char* const &sName) const { + return this->str == sName; + } + inline bool operator != (const char* const &sName) const { + return this->str != sName; + } + + private: + explicit Name(const char* const &sName); + void setOrderIndex(); + }; + + std::ostream& operator<<(std::ostream& os, const RBX::Name& name); + + // An object that has a Name + class RBXInterface INamed { + public: + virtual const Name& getName() const = 0; + }; + + // A template implementation of INamed + template + class Named : public BaseClass { + public: + + // Constructors with different numbers of arguments + Named() : BaseClass() {} + template + Named(Arg0 arg0) : BaseClass(arg0) {} + template + Named(Arg0 arg0, Arg1 arg1) : BaseClass(arg0, arg1) {} + template + Named(Arg0 arg0, Arg1 arg1, Arg2 arg2) : BaseClass(arg0, arg1, arg2) {} + template + Named(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3) : BaseClass(arg0, arg1, arg2, arg3) {} + + static const Name& name() { + return Name::declare(); + } + virtual const Name& getName() const { + return name(); + } + }; + +} + +#endif diff --git a/App/util/NamedMutex.h b/App/util/NamedMutex.h new file mode 100644 index 0000000..95b8a68 --- /dev/null +++ b/App/util/NamedMutex.h @@ -0,0 +1,17 @@ +#pragma once + +#ifdef _WIN32 +#include + +namespace RBX +{ +class ScopedNamedMutex +{ + HANDLE hMutex; + +public: + ScopedNamedMutex(const char* name); + ~ScopedNamedMutex(); +}; +} // namespace RBX +#endif // #ifdef _WIN32 \ No newline at end of file diff --git a/App/util/NavKeys.h b/App/util/NavKeys.h new file mode 100644 index 0000000..b0bebbc --- /dev/null +++ b/App/util/NavKeys.h @@ -0,0 +1,86 @@ +#pragma once + +namespace RBX { + + class NavKeys { + public: + bool forward_arrow; + bool backward_arrow; + bool left_arrow; + bool right_arrow; + bool forward_asdw; + bool backward_asdw; + bool left_asdw; + bool right_asdw; + bool strafe_left_q; + bool strafe_right_e; + bool space; + bool backspace; + bool shift; + + NavKeys() : forward_arrow(false), + backward_arrow(false), + left_arrow(false), + right_arrow(false), + forward_asdw(false), + backward_asdw(false), + left_asdw(false), + right_asdw(false), + strafe_left_q(false), + strafe_right_e(false), + space(false), + backspace(false), + shift(false) + {} + + bool forward() const {return (forward_arrow || forward_asdw);} + + bool backward() const {return (backward_arrow || backward_asdw);} + + bool left() const {return (left_arrow || left_asdw);} + + bool right() const {return (right_arrow || right_asdw);} + + bool up() const {return strafe_left_q;} + + bool down() const {return strafe_right_e;} + + bool backspaceDown() const {return backspace;} + + bool arrowKeyDown() const {return (forward_arrow || backward_arrow || left_arrow || right_arrow);} + + bool asdwKeyDown() const {return (forward_asdw || backward_asdw || left_asdw || right_asdw);} + + bool qeKeyDown() const {return (strafe_left_q || strafe_right_e);} + + bool navKeyDown() const {return (arrowKeyDown() || asdwKeyDown() || qeKeyDown() || space) || backspaceDown();} + + int leftRightASDW() const {return left_asdw ? 1 : (right_asdw ? -1 : 0);} + + int strafeQE() const {return strafe_left_q ? 1 : (strafe_right_e ? -1 : 0);} + + int leftRightArrow() const { + return left_arrow ? 1 : (right_arrow ? -1 : 0); + } + + int forwardBackwardArrow() const { + return forward_arrow ? 1 : (backward_arrow ? -1 : 0); + } + + int forwardBackwardASDW() const { + return forward_asdw ? 1 : (backward_asdw ? -1 : 0); + } + + int strafeLeftRightQE() const { + return strafe_left_q ? 1 : (strafe_right_e ? -1 : 0); + } + + bool shiftKeyDown() const + { + return shift; + } + + + }; + +} // namespace \ No newline at end of file diff --git a/App/util/NormalId.h b/App/util/NormalId.h new file mode 100644 index 0000000..9f2a3f0 --- /dev/null +++ b/App/util/NormalId.h @@ -0,0 +1,69 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/G3DCore.h" + +namespace RBX { + + enum NormalIdMask + { + NORM_NONE_MASK = 0x00, + NORM_X_MASK = 0x01, + NORM_Y_MASK = 0x02, + NORM_Z_MASK = 0x04, + NORM_X_NEG_MASK = 0x08, + NORM_Y_NEG_MASK = 0x10, + NORM_Z_NEG_MASK = 0x20, + NORM_ALL_MASK = 0x3f + }; + enum NormalId { NORM_X = 0, + NORM_Y, + NORM_Z, + NORM_X_NEG, + NORM_Y_NEG, + NORM_Z_NEG, + NORM_UNDEFINED}; + + + bool validNormalId(NormalId normalId); + + NormalIdMask normalIdToMask(NormalId normal); + + NormalId normalIdOpposite(NormalId normalId); + NormalId normalIdToU(NormalId normalId); + NormalId normalIdToV(NormalId normalId); + + const Vector3& normalIdToVector3(NormalId normalId); // Vector pointing along the normal direction + const Matrix3& normalIdToMatrix3(NormalId normalId); // Z axis is away from face + + NormalId Vector3ToNormalId(const Vector3& v); + NormalId Matrix3ToNormalId(const Matrix3& m); + NormalId intToNormalId(int i); + + Vector3 uvwToObject(const Vector3& uvwPt, NormalId faceId); + Vector3 objectToUvw(const Vector3& objectPt, NormalId faceId); + + template + Vector3 uvwToObject(const Vector3& v); + + template + Vector3 objectToUvw(const Vector3& v); + + + + // LEGACY - Deprecated + // old stuff - need to inspect and see if it is really objectToUvw or uvwToObject + Vector3 mapToUvw_Legacy(const Vector3& ptInObject, NormalId normalId); + + template + Vector3 faceMap_Legacy(const Vector3& v) { + return uvwToObject(v); + } + + template + Vector3 faceMap_Legacy(float x, float y, float z) { + return faceMap_Legacy(Vector3(x, y, z)); + } + +} // namespace diff --git a/App/util/Object.h b/App/util/Object.h new file mode 100644 index 0000000..9f7e6fc --- /dev/null +++ b/App/util/Object.h @@ -0,0 +1,375 @@ +/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "rbx/Debug.h" +#include "Util/Name.h" +#include +#include + +#include "Security/ApiSecurity.h" +#include "Security/FuzzyTokens.h" +#include "V8DataModel/HackDefines.h" + +#include "rbx/boost.hpp" +#include "boost/weak_ptr.hpp" +#include "boost/shared_ptr.hpp" +#include "boost/scoped_ptr.hpp" +#include "boost/enable_shared_from_this.hpp" + +using boost::shared_ptr; +using boost::scoped_ptr; +using boost::weak_ptr; +using boost::enable_shared_from_this; + + +namespace RBX +{ + namespace Reflection + { + class DescribedBase; + } + + enum CreatorRole + { + ReplicationCreator, + SerializationCreator, + ScriptingCreator, + EngineCreator + }; + + + template boost::shared_ptr shared_polymorphic_downcast(const boost::shared_ptr& r) + { + BOOST_ASSERT(dynamic_cast(r.get()) == r.get()); + return boost::static_pointer_cast(r); + } + + template boost::shared_ptr shared_dynamic_cast(const boost::shared_ptr& r) + { + return boost::dynamic_pointer_cast(r); + } + + template boost::shared_ptr shared_static_cast(const boost::shared_ptr& r) + { + return boost::static_pointer_cast(r); + } + + // Use this to convert an object to its corresponding boost::shared_ptr + template boost::shared_ptr shared_from(T* r) + { + return r ? boost::static_pointer_cast(r->shared_from_this()) : boost::shared_ptr(); + } + + template weak_ptr weak_from(T* r) + { + return r ? boost::static_pointer_cast(r->shared_from_this()) : boost::shared_ptr(); + } + + // Use this to downcast an object to a shared_ptr + template shared_ptr shared_from_polymorphic_downcast(enable_shared_from_this* r) + { + return r ? shared_polymorphic_downcast(r->shared_from_this()) : shared_ptr(); + } + + template shared_ptr shared_from_static_cast(enable_shared_from_this* r) + { + return r ? shared_static_cast(r->shared_from_this()) : shared_ptr(); + } + + template shared_ptr shared_from_dynamic_cast(enable_shared_from_this* r) + { + return r ? shared_dynamic_cast(r->shared_from_this()) : shared_ptr(); + } + + template inline bool weak_equal(const weak_ptr& lhs, const weak_ptr& rhs) + { + return !(lhs < rhs) && !(rhs < lhs); + } + + class RBXInterface ICreator + { + public: + virtual shared_ptr create() const = 0; + }; + + template + class RBXBaseClass Creatable + { + public: + class Deleter + { + public: + void operator()(Class* instance) + { + Class::predelete(instance); + delete instance; + } + }; + + template + static shared_ptr create() + { + shared_ptr obj = shared_ptr(new T(), Deleter()); + shared_ptr (*thisFunction)() = &create; + checkRbxCaller >(reinterpret_cast(thisFunction)); + return obj; + } + template + static shared_ptr create(P1 p1) + { + shared_ptr obj = shared_ptr(new T(p1), Deleter()); + shared_ptr (*thisFunction)(P1) = &create; + checkRbxCaller >(reinterpret_cast(thisFunction)); + return obj; + } + template + static shared_ptr create(P1 p1, P2 p2) + { + shared_ptr obj = shared_ptr(new T(p1, p2), Deleter()); + shared_ptr (*thisFunction)(P1, P2) = &create; + checkRbxCaller >(reinterpret_cast(thisFunction)); + return obj; + } + template + static shared_ptr create(P1 p1, P2 p2, P3 p3) + { + shared_ptr obj = shared_ptr(new T(p1, p2, p3), Deleter()); + shared_ptr (*thisFunction)(P1, P2, P3) = &create; + checkRbxCaller >(reinterpret_cast(thisFunction)); + return obj; + } + template + static shared_ptr create(P1 p1, P2 p2, P3 p3, P4 p4) + { + shared_ptr obj = shared_ptr(new T(p1, p2, p3, p4), Deleter()); + shared_ptr (*thisFunction)(P1, P2, P3, P4) = &create; + checkRbxCaller >(reinterpret_cast(thisFunction)); + return obj; + } + template + static shared_ptr create(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + { + shared_ptr obj = shared_ptr(new T(p1, p2, p3, p4, p5), Deleter()); + shared_ptr (*thisFunction)(P1, P2, P3, P4, P5) = &create; + checkRbxCaller >(reinterpret_cast(thisFunction)); + return obj; + } + template + static shared_ptr create(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + { + shared_ptr obj = shared_ptr(new T(p1, p2, p3, p4, p5, p6), Deleter()); + shared_ptr (*thisFunction)(P1, P2, P3, P4, P5, P6) = &create; + checkRbxCaller >(reinterpret_cast(thisFunction)); + return obj; + } + template + static shared_ptr create(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) + { + shared_ptr obj = shared_ptr(new T(p1, p2, p3, p4, p5, p6, p7), Deleter()); + shared_ptr (*thisFunction)(P1, P2, P3, P4, P5, P6, P7) = &create; + checkRbxCaller >(reinterpret_cast(thisFunction)); + return obj; + } + + static std::map& getCreators() + { + // TODO: replace with a faster lookup map, like the Loki AssocVector? + static std::map creators; + return creators; + } + + static shared_ptr createByName(const Name& name, CreatorRole creatorRole) + { + std::map::iterator iter = getCreators().find(&name); + if (iter!=getCreators().end()){ + if(shared_ptr result = shared_polymorphic_downcast(iter->second->create())){ + switch(creatorRole) + { + case ReplicationCreator: + return result; + case SerializationCreator: + if(result->getDescriptor().isSerializable()) + return result; + break; + case ScriptingCreator: + if(result->getDescriptor().isScriptCreatable()) + return result; + break; + case EngineCreator: + return result; + } + } + } + return shared_ptr(); + } + + // Get object creator by className + static const ICreator* getCreator(const Name& name) + { + std::map::iterator iter = getCreators().find(&name); + return (iter!=getCreators().end()) ? iter->second : NULL; + } + + private: + Creatable(); // this is a utility class + }; + + template + class FactoryProduct : public BaseClass { + class Creator : public ICreator { + private: + static int isConstructedTrue() {return 666;} + static int isConstructed; // debugging - if constructed, == 666 + + const RBX::Name& getClassNameUnconstructed() const { + return RBX::Name::declare(); + } + + public: + static bool wasConstructed() {return isConstructed == isConstructedTrue();} + + /* override */ shared_ptr create() const { + RBXASSERT(wasConstructed()); + return Creatable::template create(); + } + + const RBX::Name& getClassName() const { + RBXASSERT(wasConstructed()); + return RBX::Name::declare(); + } + + Creator() + { + // Register this creator for create-by-className + const RBX::Name& name = getClassNameUnconstructed(); + + auto& creators = Creatable::getCreators(); + + RBXASSERT(creators.find(&name)==creators.end()); + RBXASSERT(!wasConstructed()); + + creators[&name] = this; + isConstructed = isConstructedTrue(); + + RBXASSERT(creators.find(&name)!=creators.end()); + RBXASSERT(wasConstructed()); + } + + ~Creator() { + auto& creators = Creatable::getCreators(); + + RBXASSERT(wasConstructed()); + creators.erase(&getClassName()); + } + }; + private: +#ifdef _WIN32 + static const Creator creatorPrivate; +#else + static Creator creatorPrivate; +#endif + + protected: + FactoryProduct() + { + } + template + FactoryProduct(Arg0 arg0):BaseClass(arg0) + { + } + template + FactoryProduct(Arg0 arg0, Arg1 arg1):BaseClass(arg0,arg1) + { + } + virtual ~FactoryProduct() + { + } + static const Creator& static_getCreator() { + RBXASSERT(Creator::wasConstructed()); + return creatorPrivate; + } + public: + const ICreator& getCreator() { + return static_getCreator(); + } + + static const RBX::Name& className() { return static_getCreator().getClassName(); }; + static bool isNullClassName() { + RBXASSERT(!className().empty()); + return false; + }; + const RBX::Name& getClassName() const { return static_getCreator().getClassName(); }; + + // Convenient static functions for creating an instance of this class: + // TODO: Refactor: rename createInstance --> create, but also need to rename AbstractFactoryProduct::create to something else + static shared_ptr createInstance() { + return Creatable::template create(); + } + template + static shared_ptr createInstance(P1 p1) + { + return Creatable::template create(p1); + } + template + static shared_ptr createInstance(P1 p1, P2 p2) + { + return Creatable::template create(p1, p2); + } + template + static shared_ptr createInstance(P1 p1, P2 p2, P3 p3) + { + return Creatable::template create(p1, p2, p3); + } + template + static shared_ptr createInstance(P1 p1, P2 p2, P3 p3, P4 p4) + { + return Creatable::template create(p1, p2, p3, p4); + } + }; + + // Static Defination Go Here + // creatorPrivate was a const earlier, but that gives gcc error while trying to define the variable with const qualification. + // gcc error: expected nested-name-specifier before 'const' + // This is not a proper way to fix, but I do not have a choice right now. The variable is in a private section of a class so should be safe. + template +#ifdef _WIN32 + typename const FactoryProduct::Creator FactoryProduct::creatorPrivate; +#else + typename FactoryProduct::Creator FactoryProduct::creatorPrivate; +#endif + + template + int FactoryProduct::Creator::isConstructed; + + // For objects that should NOT be creatable by a factory + template + class NonFactoryProduct : public BaseClass + { + public: + NonFactoryProduct():BaseClass() {} + + template + NonFactoryProduct(Arg0 arg0):BaseClass(arg0) {} + template + NonFactoryProduct(Arg0 arg0, Arg1 arg1):BaseClass(arg0, arg1) {} + template + NonFactoryProduct(Arg0 arg0, Arg1 arg1, Arg2 arg2):BaseClass(arg0, arg1, arg2) {} + template + NonFactoryProduct(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3):BaseClass(arg0, arg1, arg2, arg3) {} + + static const RBX::Name& className() + { + return RBX::Name::declare(); + }; + + static bool isNullClassName() { + RBXASSERT(className().empty() == (sClassName==NULL)); + return sClassName==NULL; + }; + const RBX::Name& getClassName() const { + return className(); + } + }; +} // namespace RBX + diff --git a/App/util/ObscureValue.h b/App/util/ObscureValue.h new file mode 100644 index 0000000..9aa7e9a --- /dev/null +++ b/App/util/ObscureValue.h @@ -0,0 +1,58 @@ +#pragma once + +#include "FastLog.h" + + +namespace RBX { + + // Wrapper around values that allows it to be used like + // a normal reference to the type (i.e. T& instead of T*). + // Also mildly obscures stored values so that they are harder to find + // with a memory scan. + template class ObscureValue { + static const int kArraySize = (sizeof(T)/sizeof(long) < 1) ? 1 : sizeof(T)/sizeof(long); + union InternalStorage + { + long asRaw[kArraySize]; + T asBase; + }; + InternalStorage storage; + public: + explicit ObscureValue(const T& value){ + InternalStorage tmp; + tmp.asBase = value; + for (int i = 0; i < kArraySize; ++i) + { + storage.asRaw[i] = tmp.asRaw[i] ^ reinterpret_cast(this) ; + } + } + + operator const T() const { + InternalStorage tmp; + for (int i = 0; i < kArraySize; ++i) + { + tmp.asRaw[i] = storage.asRaw[i] ^ reinterpret_cast(this) ; + } + return tmp.asBase; + } + + ObscureValue& operator=(const T& other) { + InternalStorage tmp; + tmp.asBase = other; + for (int i = 0; i < kArraySize; ++i) + { + storage.asRaw[i] = tmp.asRaw[i] ^ reinterpret_cast(this) ; + } + return *this; + } + private: + // Disable no-arg construction, copy, and regular assign. + // Some of these may be safe, but they are not needed yet, + // and the safety of this class is easier to understand without + // them. + ObscureValue(); + ObscureValue(const ObscureValue&); + ObscureValue& operator=(const ObscureValue&); + }; + +} \ No newline at end of file diff --git a/App/util/PV.h b/App/util/PV.h new file mode 100644 index 0000000..ef3be78 --- /dev/null +++ b/App/util/PV.h @@ -0,0 +1,108 @@ +#pragma once + +#include "Util/Velocity.h" + +namespace RBX { + + class PV + { + public: + CoordinateFrame position; + Velocity velocity; + + private: +/* + inline PV operator *(const PV& localPV) const { + CoordinateFrame worldPos(position * localPV.position); + Velocity otherVWorld = localPV.velocity.rotateBy(position.rotation); + Vector3 linearVel = linearVelocityAtPoint(worldPos.translation) + otherVWorld.linear; + Vector3 rotVel = velocity.rotational + otherVWorld.rotational; + Velocity worldVel(linearVel, rotVel); + return PV(worldPos, worldVel); + } +*/ + + public: + + bool operator==(const PV& other) const { + return (position == other.position) && (velocity == other.velocity); + } + + bool operator!=(const PV& other) const { + return !(*this == other); + } + + // CoordinateFrame and Velocity both initialize to identity/0 + inline PV() + {} + + PV(const CoordinateFrame& _position, const Velocity& _velocity) : + position(_position), velocity(_velocity) {} + + PV(const PV &other) : + position(other.position), velocity(other.velocity) {} + + inline ~PV() {} + + /** + Computes the inverse of this PV. + */ +/* + inline PV inverse() const { + PV out; + out.position = position.inverse(); + out.velocity = -velocity.rotateBy(out.position.rotation); + return out; + } + + inline PV toObjectSpace(const PV& g) const { + return this->inverse() * g; + } + + inline PV toWorldSpace(const PV& localPV) const { + return *this * localPV; + } +*/ + + // Generate Local Linear Velocities + + inline Vector3 linearVelocityAtPoint(const Vector3& worldPos) const { + return velocity.linearVelocityAtOffset(worldPos - position.translation); + } + + + // Generate Local Velocities + + inline Velocity velocityAtPoint(const Vector3& worldPos) const { + return velocity.velocityAtOffset(worldPos - position.translation); + } + + inline Velocity velocityAtLocalOffset(const Vector3& localOffset) const { + Vector3 worldPos = position.pointToWorldSpace(localOffset); + return velocityAtPoint(worldPos); + } + + // Generate Local PVs + inline PV pvAtLocalOffset(const Vector3& localOffset) const { + return pvAtLocalCoord(CoordinateFrame(localOffset)); + } + + static inline void pvAtLocalCoord(const PV& base, const CoordinateFrame& localCoord, PV& answer) { + CoordinateFrame::mul(base.position, localCoord, answer.position); + answer.velocity = base.velocityAtPoint(answer.position.translation); + } + + inline PV pvAtLocalCoord(const CoordinateFrame& localCoord) const { + PV answer; + pvAtLocalCoord(*this, localCoord, answer); + return answer; + } + + inline PV lerp(const PV& other, float alpha) const { + return PV( position.lerp(other.position, alpha), + velocity.lerp(other.velocity, alpha) ); + } + + }; + +} // namespace RBX diff --git a/App/util/PartMaterial.h b/App/util/PartMaterial.h new file mode 100644 index 0000000..9c7bf5a --- /dev/null +++ b/App/util/PartMaterial.h @@ -0,0 +1,40 @@ +#pragma once + +namespace RBX { + +enum PartMaterial +{ + PLASTIC_MATERIAL = 0x0100, + SMOOTH_PLASTIC_MATERIAL = 0x0110, + NEON_MATERIAL = 0x0120, + WOOD_MATERIAL = 0x0200, + WOODPLANKS_MATERIAL = 0x0210, + MARBLE_MATERIAL = 0x0310, + SLATE_MATERIAL = 0x0320, + CONCRETE_MATERIAL = 0x0330, + GRANITE_MATERIAL = 0x0340, + BRICK_MATERIAL = 0x0350, + PEBBLE_MATERIAL = 0x0360, + COBBLESTONE_MATERIAL= 0x0370, + ROCK_MATERIAL = 0x0380, + SANDSTONE_MATERIAL = 0x0390, + BASALT_MATERIAL = 0x0314, + CRACKED_LAVA_MATERIAL = 0x0324, + RUST_MATERIAL = 0x0410, + DIAMONDPLATE_MATERIAL = 0x0420, + ALUMINUM_MATERIAL = 0x0430, + METAL_MATERIAL = 0x0440, + GRASS_MATERIAL = 0x0500, + SAND_MATERIAL = 0x0510, + FABRIC_MATERIAL = 0x0520, + SNOW_MATERIAL = 0x0530, + MUD_MATERIAL = 0x0540, + GROUND_MATERIAL = 0x0550, + ICE_MATERIAL = 0x0600, + GLACIER_MATERIAL = 0x0610, + AIR_MATERIAL = 0x0700, + WATER_MATERIAL = 0x0800, + LEGACY_MATERIAL = 0xFFFF, // should not be serialized +}; + +} diff --git a/App/util/PathInterpolatedCFrame.h b/App/util/PathInterpolatedCFrame.h new file mode 100644 index 0000000..d720ce3 --- /dev/null +++ b/App/util/PathInterpolatedCFrame.h @@ -0,0 +1,101 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "rbx/rbxTime.h" +#include +#include "Util/Average.h" +#include "Util/Velocity.h" +#include "rbx/RunningAverage.h" + +namespace RBX { + + #define NUM_MAX_HISTORY 8 + #define NUM_BUFFER_NODES 2 + + class PartInstance; + + // This class keeps a list of frames and the time they were set. + + class PathInterpolatedCFrame + { + private: + + struct FrameInfo + { + CoordinateFrame coordinateFrame; + Velocity velocity; + RemoteTime remoteTime; // in sender's time scale + FrameInfo() {} + FrameInfo(const CoordinateFrame &cf, const Velocity& vel, const Time& local, const RemoteTime& remote) : coordinateFrame(cf), velocity(vel), remoteTime(remote) {} + }; + + FrameInfo prevFrame; + FrameInfo lastStartFrame; + + bool beingMoved; + int uiStepId; + double localToRemoteTimeOffset; // subtract local time by this value to get remote time + RunningAverage<> avgInterval; + Time prevStepTime; + boost::circular_buffer_space_optimized frameInfos; + + float targetDelayInSeconds; + int targetFrame; + + // for analytics + double lastTargetDelayValue; + double targetDelayDeltaMax; + + + inline const CoordinateFrame& recordAndReturn(const CoordinateFrame& value, const Time& local, const RemoteTime& remote) + { + beingMoved = true; + prevFrame.coordinateFrame = value; + prevFrame.remoteTime = remote; + return prevFrame.coordinateFrame; + } + + inline const CoordinateFrame& recordAndReturnHermite(const CoordinateFrame& value, const FrameInfo& startFrame, const Time& local, const RemoteTime& remote) + { + beingMoved = true; + lastStartFrame = startFrame; + prevFrame.coordinateFrame = value; + prevFrame.remoteTime = remote; + return prevFrame.coordinateFrame; + } + + const CoordinateFrame& interpolate( const Time& now, const Time& targetTime, const unsigned int& upper, const PartInstance* part = NULL); + const CoordinateFrame& interpolateHermiteSpline( const Time& now, const Time& targetTime, const unsigned int& upper, const PartInstance* part = NULL); + RemoteTime computeSampleTargetTime( const Time& now); + + public: + PathInterpolatedCFrame(); + ~PathInterpolatedCFrame() {} + + void clearHistory(); + + // timeStamp is time this value was set. If coming from network, the value should be time it was send from the server + void setValue(PartInstance* part, const CoordinateFrame& value, const Velocity& vel, const RemoteTime& timeStamp, Time now, float localTimeOffest, int numNodesAhead); + + void setTargetDelay(float value); + + void setUiStepId(int id) {uiStepId = id;} + int getUiStepId() const {return uiStepId;} + + double getLocalToRemoteTimeOffset() {return localToRemoteTimeOffset;} + + void setRenderedFrame(const CoordinateFrame& value); + void setRenderedFrame(const CoordinateFrame& value, const RemoteTime& remoteTime); + + CoordinateFrame computeValue(PartInstance* part, const Time& t); + CoordinateFrame getLastComputedValue() const { return prevFrame.coordinateFrame; } + + bool isBeingMovedByInterpolator() const { return beingMoved; } + + Color3 getSampleIntervalColor() const; + float getSampleInterval() const; + + void renderPath(Adorn* adorn); + }; + +} // namespace diff --git a/App/util/PhysicalProperties.h b/App/util/PhysicalProperties.h new file mode 100644 index 0000000..774fff8 --- /dev/null +++ b/App/util/PhysicalProperties.h @@ -0,0 +1,117 @@ +#pragma once +#include +#include "Util/Math.h" + +namespace RBX { + +enum PhysicalPropertiesMode +{ + PhysicalPropertiesMode_Legacy, + PhysicalPropertiesMode_Default, + PhysicalPropertiesMode_NewPartProperties +}; + + +class PhysicalProperties +{ +private: + bool customEnabled; + float density; + float elasticity; + float friction; + float frictionWeight; + float elasticityWeight; + + static float minDen() { return 0.01f; } + static float maxDen() { return 100.0f;} + static float minFri() { return 0.0f; } // Negative friction Generates energy + static float maxFri() { return 2.0f; } + static float minFrW() { return 0.0f; } + static float maxFrW() { return 100.0f;} + static float minEla() { return 0.0f; } // Negative Elasticity causes penetration + static float maxEla() { return 1.0f; } // Elasticity > 1 causes energy gain + static float minElW() { return 0.0f; } + static float maxElW() { return 100.0f;} + + +public: + // Default Constructor for initializing part instances + PhysicalProperties(): + customEnabled(false), + density(0), + friction(0), + elasticity(0), + frictionWeight(0), + elasticityWeight(0) + { + } + + // Constructor for enabling Custom + PhysicalProperties(float density_, float friction_, float elasticity_, float frictionWeight_ = 1.0f, float elasticityWeight_ = 1.0f): + customEnabled(true), + density (G3D::clamp(density_, minDen(), maxDen())), + friction (G3D::clamp(friction_, minFri(), maxFri())), + elasticity (G3D::clamp(elasticity_, minEla(), maxEla())), + frictionWeight (G3D::clamp(frictionWeight_, minFrW(), maxFrW())), + elasticityWeight(G3D::clamp(elasticityWeight_, minElW(), maxElW())) + { + } + + size_t hashCode() const; + + bool getCustomEnabled() const + { + return customEnabled; + } + + void setCustomEnabled( bool value ) + { + customEnabled = value; + } + + float getDensity() const + { + return density; + } + + float getFriction() const + { + return friction; + } + + float getElasticity() const + { + return elasticity; + } + + float getFrictionWeight() const + { + return frictionWeight; + } + + float getElasticityWeight() const + { + return elasticityWeight; + } + + //Operators + + bool operator==(const PhysicalProperties& other) const + { + return ((customEnabled == other.customEnabled) && + (density == other.density) && + (friction == other.friction) && + (elasticity == other.elasticity) && + (frictionWeight == other.frictionWeight) && + (elasticityWeight== other.elasticityWeight)); + } + + bool operator!=(const PhysicalProperties& other) const + { + return !(*this == other); + } +}; + +size_t hash_value(const PhysicalProperties& properties); + +}; //Namespace RBX \ No newline at end of file diff --git a/App/util/PhysicsCoord.h b/App/util/PhysicsCoord.h new file mode 100644 index 0000000..3f26691 --- /dev/null +++ b/App/util/PhysicsCoord.h @@ -0,0 +1,68 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "Util/Quaternion.h" + +namespace RBX { + + class PhysicsCoord + { + public: + Vector3 translation; + Quaternion rotation; + + bool operator==(const PhysicsCoord& other) const { + return (translation == other.translation) && (rotation == other.rotation); + } + + bool operator!=(const PhysicsCoord& other) const { + return !(*this == other); + } + + inline PhysicsCoord() : + translation(Vector3::zero()) {} + + PhysicsCoord(const CoordinateFrame& cframe) : + translation(cframe.translation), rotation(cframe.rotation) + {} + + PhysicsCoord(const Vector3& _translation) : + translation(_translation) {} + + PhysicsCoord(const Vector3& _translation, const Quaternion& _rotation) : + translation(_translation), rotation(_rotation) {} + + PhysicsCoord(const PhysicsCoord &other) : + translation(other.translation), rotation(other.rotation) {} + + PhysicsCoord operator+ (const PhysicsCoord& rhs) const { + return PhysicsCoord(translation + rhs.translation, rotation + rhs.rotation); + } + + PhysicsCoord operator- (const PhysicsCoord& rhs) const { + return PhysicsCoord(translation - rhs.translation, rotation - rhs.rotation); + } + + float squaredMagnitude() const { + return translation.squaredMagnitude() + rotation.magnitude(); // note for quaternion magnitude == squared values?.... + } + + PhysicsCoord& operator+= (const PhysicsCoord& other) { + translation += other.translation; + rotation += other.rotation; + return *this; + } + + PhysicsCoord operator*(float f) const { + return PhysicsCoord(translation * f, rotation * f); + } + + PhysicsCoord operator/(float f) const { + float mul = 1.0f/f; + return *this * mul; + } + + }; + + +} // namespace RBX diff --git a/App/util/Profiling.h b/App/util/Profiling.h new file mode 100644 index 0000000..560b992 --- /dev/null +++ b/App/util/Profiling.h @@ -0,0 +1,106 @@ +#pragma once + +#include "rbx/boost.hpp" +#include "rbx/rbxTime.h" +#include "boost/array.hpp" +#include +#include + +namespace RBX +{ + namespace Profiling + { + void init(bool enabled); + void setEnabled(bool enabled); + bool isEnabled(); + + struct Bucket + { + float sampleTimeElapsed; // System time span that the bucket sampled for + float wallTimeSpan; + int frames; // The number of "frames" recorded in the bucket + + double getActualFPS() const; // frames/sec + double getNominalFPS() const; // frames/sec + double getNominalFramePeriod() const; // secs/frame + + double getSampleTime() const { return sampleTimeElapsed; } + double getWallTime() const { return wallTimeSpan; } + int getFrames() const { return frames; } + + Bucket(); + Bucket& operator+=(const Bucket& b); + }; + + class Profiler : public boost::noncopyable + { + protected: + const Time::Interval bucketTimeSpan; // The minimum amount of time per bucket + int currentBucket; + boost::array buckets; // TODO: Use boost::circular_buffer + Time lastSampleTime; + public: + const std::string name; + Profiler(const char* name); + virtual ~Profiler() {}; + Bucket getWindow(double window) const; // get last samples based on elapsed time + Bucket getFrames(int frames) const; // get n last frames. + static double profilingWindow; + }; + + // Profiles sections of code using the Mark class + class CodeProfiler : public Profiler + { + friend class Mark; + public: + CodeProfiler(const char* name); + private: + void log(bool frameTick, double wallTimeElapsed); + void unlog(double wallTimeElapsed); + }; + + // Mark a section of code as belonging to a CodeProfiler + // (Enclosing Mark will be disabled for the lifetime of this object) + class Mark + { + CodeProfiler& section; + Mark* enclosingMark; + bool frameTick; + Time startTime; + const bool enabled; + Time::Interval childrenElapsed; // sum of elapsed time (inclusive) in all Markers that have _this_ as a enclosingMark. + const bool logInclusive; // true if you want to log time including children. false will subtract time spent in children + public: + Mark(CodeProfiler& section, bool frameTick, bool logInclusive = false); + ~Mark(); + }; + + + // BucketProfile + class BucketProfile + { + std::vector data; + const int* bucketLimits; + + unsigned int findBucket(int v); + int total; + + public: + // WARNING: assumes pointer to bucketLimits is static and saves it directly + BucketProfile(const int* bucketLimits, int size); + + BucketProfile(const BucketProfile& rhs); + BucketProfile(); + const BucketProfile& operator = (const BucketProfile& rhs); + + void addValue(int v); + void removeValue(int v); + void clear(); + + int getTotal() { return total; } + + const std::vector& getData() const { return data; }; + const int* getLimits() const { return bucketLimits; }; + }; + } +}; diff --git a/App/util/ProgramMemoryChecker.h b/App/util/ProgramMemoryChecker.h new file mode 100644 index 0000000..5d9946b --- /dev/null +++ b/App/util/ProgramMemoryChecker.h @@ -0,0 +1,366 @@ +#pragma once + +#include +#include "rbx/rbxTime.h" +#include +#include "Security/RandomConstant.h" +#include "v8datamodel/HackDefines.h" +#include "util/HeapValue.h" +#include "Security/ApiSecurity.h" + +namespace RBX { + +namespace Hasher +{ + // The PMC constructor assumes a specific ordering of these items. + enum HashSection + { + kGoldHashStart = 0, + kGoldHashEnd = 1, + kGoldHashRot = 2, + kRdataHash = 3, + kVmpPlainHash = 4, + kVmpMutantHash = 5, + kIatHash = 6, + kMiscHash = 7, + kMsvcHash = 8, + kVmp0MiscHash = 9, + kVmp1MiscHash = 10, + kNonGoldHashRot = 11, + kNumberOfSectionHashes = 12, + kGoldHashStruct = 12, + kAllHashStruct = 13, + kNumberOfHashes = 14 + }; + + // These do not have a 1:1 relation with the indicies above. + enum HashFailures + { + // 0x?000 + kVmp1MiscHashFail = 1<<15, + kVmp0MiscHashFail = 1<<14, + kVmpMutantHashFail = 1<<13, + kIatHashFail = 1<<12, + // 0x0?00 + kGoldHashFail = 1<<11, + kNonceFail = 1<<10, + kAllHashStructFail = 1<<9, + kGoldHashStructFail = 1<<8, + // 0x00?0 + kNonGoldHashRotFail = 1<<7, + kVmpPlainHashFail = 1<<6, + kMsvcHashFail = 1<<5, + kRdataHashFail = 1<<4, + // 0x000? + kMiscHashFail = 1<<3, + kGoldHashRotFail = 1<<2, + kGoldHashEndFail = 1<<1, + kGoldHashStartFail = 1<<0 + }; + + static const unsigned int kGoldHashMask = kGoldHashFail; + + static const unsigned int kDiffHashMask = kGoldHashStartFail | kGoldHashEndFail + | kMiscHashFail | kRdataHashFail | kMsvcHashFail | kGoldHashStructFail + | kAllHashStructFail | kVmpPlainHashFail | kVmpMutantHashFail | kVmp0MiscHashFail + | kVmp1MiscHashFail | kIatHashFail; + + static const unsigned int kMovingHashMask = kNonceFail | kGoldHashRotFail + | kNonGoldHashRotFail; + + static const int zeroPad[4] = {0,0,0,0}; + + static const unsigned int kPmcNonceGoodInc = 3692164867; + static const unsigned int kPmcNonceBadInc = 3692164869; + static const unsigned int kPmcNonceGoodIncInv = 2880154539; //0xABABABAB supplied by irc user. unimportant fact. + +} + +struct ScanRegion +{ + char* startingAddress; + unsigned int size; + + // ".text" and ".rdata" appear in several places in RAM, so it is safe to have them as literals. + // likewise, exploiters would quickly realize that .text and .rdata are scanned. + // if they change the value to something else, it would crash. + static ScanRegion getScanRegion(const char* moduleName, const char* RegionName); + + ScanRegion() : startingAddress(NULL), size(0){} + ScanRegion(const ScanRegion &initValue) : startingAddress(initValue.startingAddress), size(initValue.size){} + ScanRegion(char* startingAddress, unsigned int size) : startingAddress(startingAddress), size(size) {} +}; + +struct ScanRegionTest : ScanRegion +{ + void* hashState; + unsigned int lastHashValue; + bool closeHash; + bool useHashValueInStructHash; + bool useHashAddrSizeInStructHash; + + ScanRegionTest() : ScanRegion(), + hashState(NULL), + lastHashValue(0), + closeHash(false), + useHashValueInStructHash(true), + useHashAddrSizeInStructHash(true) {} + ScanRegionTest(ScanRegion initValue) : ScanRegion(initValue), + hashState(NULL), + lastHashValue(0), + closeHash(false), + useHashValueInStructHash(true), + useHashAddrSizeInStructHash(true) {} +}; + +struct PmcHashContainer +{ + typedef std::vector HashVector; + unsigned int nonce; + HashVector hash; + PmcHashContainer(const PmcHashContainer& init); + PmcHashContainer() : nonce(0) {} +}; + +extern PmcHashContainer pmcHash; + +#if defined(_WIN32) && !defined(RBX_PLATFORM_DURANGO) +class NtApiCaller +{ +private: + static const uintptr_t kKey = 111777; + static const unsigned kNtQvmEndToken = 0x0018C204; // sub esp, 4; ret 0x18; + static const unsigned kNtGtxEndToken = 0x0008C204; // sub esp, 4; ret 0x08; + // on windows xp, this is 0x00?8C212FF // call dword ptr [edx]; ret 0x?8 + static const unsigned kEndMask = 0xFFFFFF00; + typedef DWORD (NTAPI *NtQvmPfn)(HANDLE, PVOID, DWORD, PVOID, ULONG, PULONG); + typedef DWORD (NTAPI *NtGtxPfn)(HANDLE, PCONTEXT); + HANDLE thisProcess; + HeapValue hashEndSize; + HeapValue ntQvmAsUint; + HeapValue ntQvmCallHash; + HeapValue ntGtxAsUint; + HeapValue ntGtxCallHash; + HeapValue ntdllTextBase; + HeapValue ntdllSize; + + + static unsigned int hashFeed(unsigned int state, unsigned int value) + { + return state + _rotl((state+kKey)*(value-kKey), 7); + } + + __forceinline void initApiFunction(uintptr_t pfn, HeapValue& pfnOut, uintptr_t callTemplate, HeapValue& callHashOut, HeapValue& hashEndSizeOut, unsigned int endToken) + { + if (!pfn || (pfn - ntdllTextBase > ntdllSize)) + { + // couldn't find NtQueryVirtualMemory or it wasn't in the dll. + Tokens::apiToken.addFlagSafe(kNtApiNoApi); + } + pfnOut = pfn; + + // ZwFilterToken isn't important, but it is called in a near identical way as NtQVM + // generate a hash of how ntdll will be called. + if (callTemplate && (callTemplate - ntdllTextBase < ntdllSize)) + { + for (int i = 5; i < 32; ++i) // assume call takes < 32B on x86/WoW64 + { + unsigned int value = *reinterpret_cast(callTemplate + i); + callHashOut = hashFeed(callHashOut, value); + if((kEndMask & value) == (kEndMask & endToken)) + { + hashEndSizeOut = i; + break; + } + } + if (hashEndSizeOut == 0) + { + // didn't find the end token for some reason. + Tokens::apiToken.addFlagSafe(kNtApiNoSyscall); + } + } + else + { + // In this case, ZwFilterToken didn't exist for some reason, or wasn't in ntdll + Tokens::apiToken.addFlagSafe(kNtApiNoTemplate); + } + } + + __forceinline bool checkCaller(uintptr_t pfn, const HeapValue& callHash) + { + const unsigned char* const& funcMem = reinterpret_cast(pfn); + // probably should check to make sure this is within ntdll. + if (pfn && (pfn - ntdllTextBase < ntdllSize)) + { + bool canCall = true; + // Check Early Hooking: + // mov eax, dword 0x0000???? <- B8 ?? ?? 00 00 + if (funcMem[0] != 0xB8 || funcMem[3] != 0x00 || funcMem[4] != 0x00) + { + canCall = false; + Tokens::apiToken.addFlagSafe(kNtApiEarly); + } + + // Check hash of function + unsigned int checkHash = 0; + int endIdx = hashEndSize; + for (int i = 5; i < 32; ++i) // assume call takes < 32B on x86 and WoW64 + { + unsigned int value = *reinterpret_cast(funcMem + i); + checkHash = hashFeed(checkHash, value); + if(i == endIdx) + { + break; + } + } + if (checkHash != callHash) + { + canCall = false; + Tokens::apiToken.addFlagSafe(kNtApiHash); + } + + // This decodes and calls the function pointer + return canCall; + } + else + { + // not defined or not within ntdll. + Tokens::apiToken.addFlagSafe(kNtApiNoCall); + } + return false; + } + +public: + __forceinline DWORD virtualQuery(void* addr, MEMORY_BASIC_INFORMATION* info, size_t cb) + { + volatile DWORD result = 0; + uintptr_t pfn = ntQvmAsUint; + if (checkCaller(pfn, ntQvmCallHash)) + { + result = reinterpret_cast(pfn)(thisProcess, addr, 0, info, cb, NULL); + } + pfn = 0; + return result; + } + __forceinline DWORD getThreadContext(HANDLE thread, CONTEXT* ctx) + { + volatile DWORD result = 0; + uintptr_t pfn = ntGtxAsUint; + if (checkCaller(pfn, ntGtxCallHash)) + { + result = reinterpret_cast(pfn)(thread, ctx); + } + pfn = 0; + return result; + } + __forceinline bool isNtdllAddress(uintptr_t addr) + { + return (addr - ntdllTextBase) < ntdllSize; + } + NtApiCaller(); +}; +#endif + +class ProgramMemoryChecker +{ +protected: + unsigned int hsceHashOrReduced; + unsigned int hsceHashAndReduced; +public: + static const int kHASH_SEED_INIT = 42; + static const int kAllDone = 0xCCCCCCCC; // A number that is non-zero. + static const int kLuaLockOk = 0x1842783; + static const int kLuaLockBad = 0; + static const int kSteps = 30; + static const unsigned int kBlock = 16; + ProgramMemoryChecker(); + unsigned int bytesPerStep; + unsigned int currentRegion; + const char* currentMemory; + std::vector scanningRegions; + unsigned int lastCompletedHash; + unsigned int lastGoldenHash; + Time lastCompletedTime; + + unsigned int step(); + unsigned int getLastCompletedHash() const; + unsigned int getLastGoldenHash() const; + Time getLastCompletedTime() const; + + void getLastHashes(PmcHashContainer::HashVector& outHashes) const; + // This is a hash of the hashes, as well as a hash of the region information. + unsigned int hashScanningRegions(size_t regions = Hasher::kNumberOfHashes-2) const; + + // return hash of HumanoidState::computeEvent, update hsceHashOrReduce. + unsigned int updateHsceHash(); + unsigned int getHsceOrHash() const; + unsigned int getHsceAndHash() const; + + // Should look at return code. + int isLuaLockOk() const; + + // Check for stealthedit. Stealthedit sets some pages to non-executable + // and then catches the resulting exception, using the opportunity + // to redirect to a modified page without disturbing the hash mechanism. + // http://www.szemelyesintegracio.hu/cheats/41-game-hacking-articles/419-stealthedit + static bool areMemoryPagePermissionsSetupForHacking(); + +}; + +#ifdef _WIN32 +_declspec(align(8)) extern const char* const maskAddr; +_declspec(align(8)) extern const char* const goldHash; +unsigned int protectVmpSections(); +#else +__attribute__((__aligned__(8))) extern const char* const maskAddr; +__attribute__((__aligned__(8))) extern const char* const goldHash; +#endif + + +namespace Security{ + // The storage for hash checker related security constants. + extern volatile const size_t rbxGoldHash; + + // The lower part of .text + extern volatile const uintptr_t rbxLowerBase; + extern volatile const size_t rbxLowerSize; + + // The upper part of .text + extern volatile const uintptr_t rbxUpperBase; + extern volatile const size_t rbxUpperSize; + + // the .rdata section + extern volatile const uintptr_t rbxRdataBase; + extern volatile const size_t rbxRdataSize; + + // the vmp sections + extern volatile const uintptr_t rbxVmpBase; + extern volatile const size_t rbxVmpSize; + + // the Import Address (thunk) Table + extern volatile const uintptr_t rbxIatBase; + extern volatile const size_t rbxIatSize; + + // the vmp sections (plain .text section) + extern volatile const uintptr_t rbxVmpPlainBase; + extern volatile const size_t rbxVmpPlainSize; + + // the vmp sections (mutation .text section) + extern volatile const uintptr_t rbxVmpMutantBase; + extern volatile const size_t rbxVmpMutantSize; + + // the vmp sections (don't know) + extern volatile const uintptr_t rbxVmp0MiscBase; + extern volatile const size_t rbxVmp0MiscSize; + + // the vmp sections (don't know) + extern volatile const uintptr_t rbxVmp1MiscBase; + extern volatile const size_t rbxVmp1MiscSize; + + // the .rdata section without IAT + extern volatile const uintptr_t rbxRdataNoIatBase; + extern volatile const size_t rbxRdataNoIatSize; +} + +} + diff --git a/App/util/ProgressIndicator.h b/App/util/ProgressIndicator.h new file mode 100644 index 0000000..3885e10 --- /dev/null +++ b/App/util/ProgressIndicator.h @@ -0,0 +1,12 @@ +#pragma once + +namespace RBX +{ + class IProgressIndicator + { + public: + // returns true if cancel requested. + virtual bool setProgess(float percent) { return step(); }; // optional + virtual bool step() = 0; + }; +} \ No newline at end of file diff --git a/App/util/ProtectedGeneric.h b/App/util/ProtectedGeneric.h new file mode 100644 index 0000000..cc70296 --- /dev/null +++ b/App/util/ProtectedGeneric.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include +#include + +namespace RBX { + + template + class ProtectedGeneric + { + private: + Type value; + std::size_t hash; + public: + const Type& peekValue() const + { + return value; + } + bool getValue(Type& _value) const + { + _value = this->value; + + boost::hash hasher; + std::size_t newHash = hasher(_value); + return (hash == newHash); + } + void setValue(Type _value) + { + this->value = _value; + + boost::hash hasher; + hash = hasher(_value); + } + + ProtectedGeneric(Type _value) + { + setValue(_value); + } + private: + ProtectedGeneric(const ProtectedGeneric& other) + {} + }; +} diff --git a/App/util/ProtectedString.h b/App/util/ProtectedString.h new file mode 100644 index 0000000..17151b3 --- /dev/null +++ b/App/util/ProtectedString.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +struct lua_State; + +namespace RBX { + + class ProtectedString + { + public: + static const ProtectedString emptyString; + + static ProtectedString fromTrustedSource(const std::string& stringRef); + static ProtectedString fromBytecode(const std::string& stringRef); + + // Only use in unit tests! + static ProtectedString fromTestSource(const std::string& stringRef); + + ProtectedString(); + ProtectedString(const ProtectedString& other); + + const std::string& getSource() const { return source; } + const std::string& getBytecode() const { return bytecode; } + + bool empty() const { return source.empty() && bytecode.empty(); } + + const std::string& getOriginalHash() const; + void calculateHash(std::string* out) const; + + bool operator==(const ProtectedString& other) const; + bool operator!=(const ProtectedString& other) const; + + ProtectedString& operator=(const ProtectedString& other); + + private: + std::string source; + std::string bytecode; + + // Need to keep a pointer to hash to keep the size of this object in + // line with other lua-bridged types. + boost::scoped_ptr hash; + + // Hide this to force all changes in string to go through + // fromTrustedSource. + void setString(const std::string& newSource, const std::string& newBytecode); + }; + + size_t hash_value(const ProtectedString& str); + +} diff --git a/App/util/Quaternion.h b/App/util/Quaternion.h new file mode 100644 index 0000000..73b4559 --- /dev/null +++ b/App/util/Quaternion.h @@ -0,0 +1,112 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/G3DCore.h" + +namespace RBX { + +class Quaternion { +public: + float x, y, z, w; + + Quaternion(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {} + + Quaternion(const G3D::Vector3& v, float _w = 0) : x((float)v.x), y((float)v.y), z((float)v.z), w(_w) {} + + Quaternion() : x(0.0f), y(0.0f), z(0.0f), w(1.0f) {} + + Quaternion(const G3D::Matrix3& rot); + + Quaternion& operator= (const Quaternion& other); + + inline const G3D::Vector3& imag() const { + return *(reinterpret_cast(this)); + } + + inline G3D::Vector3& imag() { + return *(reinterpret_cast(this)); + } + + void toRotationMatrix( + Matrix3& rot) const; + + inline float dot(const Quaternion& other) const { + return (x * other.x) + (y * other.y) + (z * other.z) + (w * other.w); + } + + inline float magnitude() const { + return x*x + y*y + z*z + w*w; + } + + float maxComponent() const { + return std::max( std::max(fabs(x), fabs(y)), std::max(fabs(z), fabs(w))); + } + + // Return the angle of the axis-angle representation of the quaternion + inline float getAngle() const { + return 2.0f * acos(w); + } + + // Return the axis of the axis-angle representation of the quaternion + inline Vector3 getAxis() const { + float sinSquared = 1.f - w * w; + if (sinSquared < 1e-6) //Check for divide by zero + return Vector3(1.0, 0.0, 0.0); // Arbitrary + float sinRecip = 1.f/ sqrtf(sinSquared); + return Vector3(x * sinRecip, y * sinRecip, z * sinRecip); + } + + inline Quaternion conjugate() const { + return Quaternion(-x, -y, -z, w); + } + + inline float& operator[] (int i) const { + return ((float*)this)[i]; + } + + inline operator float* () { + return (float*)this; + } + + inline operator const float* () const { + return (float*)this; + } + + inline Quaternion operator*(const Quaternion& other) const { + // Following Watt & Watt, page 360 + const Vector3& v1 = imag(); + const Vector3& v2 = other.imag(); + float s1 = w; + float s2 = other.w; + return Quaternion(s1*v2 + s2*v1 + v1.cross(v2), s1*s2 - v1.dot(v2)); + } + + inline Quaternion operator+ (const Quaternion& other) const { + return Quaternion(x + other.x, y + other.y, z + other.z, w + other.w); + } + + inline Quaternion operator- (const Quaternion& other) const { + return Quaternion(x - other.x, y - other.y, z - other.z, w - other.w); + } + + inline Quaternion operator* (float s) const { + return Quaternion(s*x, s*y, s*z, s*w); + } + + // inline + Quaternion& operator*=(float fScalar); + + // inline + Quaternion& operator+=(const Quaternion& rkQuaternion); + + void normalize() { + *this *= 1.0f / sqrtf(magnitude()); + } +}; + +} // namespace + + +#include "Quaternion.inl" + diff --git a/App/util/Quaternion.inl b/App/util/Quaternion.inl new file mode 100644 index 0000000..bd72f9e --- /dev/null +++ b/App/util/Quaternion.inl @@ -0,0 +1,24 @@ +/** + Quaternion.inl + + */ + +namespace RBX { + +inline Quaternion& Quaternion::operator+= (const Quaternion& rkQuaternion) { + x += rkQuaternion.x; + y += rkQuaternion.y; + z += rkQuaternion.z; + w += rkQuaternion.w; + return *this; +} + +inline Quaternion& Quaternion::operator*= (float fScalar) { + x *= fScalar; + y *= fScalar; + z *= fScalar; + w *= fScalar; + return *this; +} + +} // namespace diff --git a/App/util/RbxStringTable.h b/App/util/RbxStringTable.h new file mode 100644 index 0000000..8a77c0a --- /dev/null +++ b/App/util/RbxStringTable.h @@ -0,0 +1,29 @@ +#pragma once + +#define STRING_BY_ID(id) (getStringById(id)) + +#ifdef _WIN32 +__declspec(noinline) const char* getStringById(int id); +#elif __APPLE__ || __ANDROID__ +__attribute__((noinline)) const char* getStringById(int id); +#else +#error Unsupported Platform. +#endif + +enum StringIDs { + ArgStringID = 0, + LuaStringStringId = 1, //"lua" + CommandOutStringId = 2, //"> %s" + StudioASHXFmt = 3, //fmt + StudioASHX = 4, //ashx + RunningScript = 5, //Running script %s + ExecScriptNewThread = 6,//Execute script in new thread, name: %s, identity: %u + FullScriptCode = 7, //Full script code:\n %s + EnableToCreateSBThread = 8, //Unable to create trusted sandbox thread + EnableToCreateNewThread = 9, //Unable to create new thread + ScriptStr = 10, //Script + Rocky = 11,//rocky + HasGamePassLuaWarning = 12, //Game passes can only be queried by a Script running on a ROBLOX game server + NoTeleportInStudio = 13, //Teleporting while using ROBLOX Studio is not permitted + LoadingScreenScriptPath = 14, // The path to the script that creates the loading gui +}; \ No newline at end of file diff --git a/App/util/Rect.h b/App/util/Rect.h new file mode 100644 index 0000000..a911004 --- /dev/null +++ b/App/util/Rect.h @@ -0,0 +1,109 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ +#pragma once + +#include "Util/G3DCore.h" +#include "G3D/Rect2D.h" + + +namespace RBX { + + // TODO: Replace with G3D::Rect2D + class Rect { + public: + typedef enum {TOP, BOTTOM, LEFT, RIGHT, CENTER, NONE} Location; + + static bool legalX(Location loc) {return ((loc != TOP) && (loc != BOTTOM));} + static bool legalY(Location loc) {return ((loc != LEFT) && (loc != RIGHT));} + + Vector2 low; // top left + Vector2 high; // bottom right + + Rect() : low(0,0), high(0,0) {} + + Rect(Rect2D r) : low(r.x0y0()), high(r.x1y1()) {} + + Rect2D toRect2D() const {return Rect2D::xyxy(low, high);} + + Rect(float left, float top, float right, float bottom) : + low(left, top), high(right, bottom) {} + + Rect(const Vector2& _high) : + low(Vector2::zero()), high(_high) {} + + Rect(const Vector2& _low, const Vector2& _high) : + low(_low), high(_high) {} + + static Rect fromLowSize(const Vector2& _low, const Vector2& _size) { + return Rect(_low, _low + _size); + } + + static Rect xywh(float x, float y, float w, float h) { + return Rect(x,y,x+w,y+h); + } + + static Rect fromCenterSize(const Vector2& _center, const Vector2& _size) { + Vector2 halfSize = _size * 0.5f; + return Rect(_center - halfSize, _center + halfSize); + } + + static Rect fromCenterSize(const Vector2& _center, float _size) { + return fromCenterSize(_center, Vector2(_size, _size)); + } + + void unionWith(const Rect& other); + + void unionWith(const Vector2& point) { + unionWith(Rect(point, point)); + } + + bool operator== (const Rect& other) const { + return ((low == other.low) && (high == other.high)); + } + + bool operator!= (const Rect& other) const { + return ((low != other.low) || (high != other.high)); + } + + bool contains(const Vector2& xz) const { + return ((xz.x >= low.x) && (xz.x <= high.x) && (xz.y >= low.y) && (xz.y <= high.y)); + } + + bool pointInRect(int x, int y) const { + return ((x >= low.x) && (x <= high.x) && (y >= low.y) && (y <= high.y)); + } + bool pointInRect(Vector2int16 point) const { + return pointInRect(point.x, point.y); + } + + Vector2 size() const { + return (high - low); + } + + Vector2 center() const { + return (low + high) * 0.5; + } + + Location pointInBorder(const Vector2& point, float borderRatio); + + Vector2 positionPoint(Location xLoc, Location yLoc) const; + + Vector2 positionPoint(const Vector2& point, Location xLoc, Location yLoc) const; + + Rect positionChild(const Rect& child, Location xLoc, Location yLoc) const; + + Rect inset(int dx) { + return Rect(low.x+dx, low.y+dx, high.x-dx, high.y-dx); + } + Rect inset(const Vector2int16& dd) { + return Rect(low.x+dd.x, low.y+dd.y, high.x-dd.x, high.y-dd.y); + } + + Vector2 clamp(const Vector2& point) { + return point.clamp(low, high); + } + + static const float BORDER_RATIO; + static const float BORDER_RATIO_DRAG; + static const float BORDER_RATIO_THIN; + }; +} \ No newline at end of file diff --git a/App/util/Region2.h b/App/util/Region2.h new file mode 100644 index 0000000..9038dd1 --- /dev/null +++ b/App/util/Region2.h @@ -0,0 +1,70 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ +#pragma once + +#include "Util/Rect.h" +#include "rbx/Debug.h" +#include "Util/Math.h" + +namespace RBX { + + class Adorn; + + class Region2 { + public: + class WeightedPoint { + public: + Vector2 point; + float radius; + + WeightedPoint() + : point(Vector2::zero()) + , radius(0.0f) + {} + + WeightedPoint(const Vector2& point, const float radius) + : point(point) + , radius(radius) + {} + }; + + private: + WeightedPoint owner; + G3D::Array others; + + bool findCloserOther(const Vector2& point, const float slop) const; + + public: + void clearEmpty() { + owner = WeightedPoint(); + others.fastClear(); + } + + bool isEmpty() const { + return (owner.radius <= 0.0f); + } + + Region2() + {} + + ~Region2() {} + + void setOwner(const WeightedPoint& _owner) { + owner = _owner; + } + + void appendOther(const WeightedPoint& _other) { + others.append(_other); + } + + bool contains(const Vector2& pos2d, const float slop) const; + + static float getRelativeError(const Vector2& pos2d, const WeightedPoint& owner); // go through all owner points - find best one + + static bool pointInRange(const Vector2& pos2d, const WeightedPoint& owner, const float slop); + + static bool closerToOtherPoint( const Vector2& pos2d, + const WeightedPoint& owner, + const WeightedPoint& other, + float slop); + }; +} \ No newline at end of file diff --git a/App/util/Region3.h b/App/util/Region3.h new file mode 100644 index 0000000..b7751b5 --- /dev/null +++ b/App/util/Region3.h @@ -0,0 +1,37 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ +#pragma once + +#include "G3D/Vector3.h" +#include "G3D/CoordinateFrame.h" + +namespace RBX { + class Extents; + + class Region3 { + private: + G3D::CoordinateFrame cframe; + Vector3 size; + void init(const Extents &extents); + + public: + Region3(); + Region3(const Vector3& min, const Vector3& max); + explicit Region3(const Extents &extents); + + ~Region3() {} + + const G3D::CoordinateFrame& getCFrame() const { return cframe; } + const Vector3& getSize() const { return size; } + + Vector3 minPos() const; + Vector3 maxPos() const; + + inline bool operator==(const Region3& other) const { + return (size == other.size) && (cframe == other.cframe); + } + + inline bool operator!=(const Region3& other) const { + return !(*this == other); + } + }; +} \ No newline at end of file diff --git a/App/util/Region3Int16.h b/App/util/Region3Int16.h new file mode 100644 index 0000000..405a672 --- /dev/null +++ b/App/util/Region3Int16.h @@ -0,0 +1,58 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ +#pragma once + +#include "G3D/Vector3int16.h" +#include "G3DCore.h" + +namespace RBX { + + class Region3int16 { + private: + Vector3int16 minPos; + Vector3int16 maxPos; + + public: + Region3int16() + { + } + + Region3int16(const Vector3int16& min, const Vector3int16& max) + : minPos(min) + , maxPos(max) + { + } + + const Vector3int16& getMinPos() const + { + return minPos; + } + + const Vector3int16& getMaxPos() const + { + return maxPos; + } + + bool operator==(const Region3int16& other) const + { + return (minPos == other.minPos) && (maxPos == other.maxPos); + } + + bool operator!=(const Region3int16& other) const + { + return !(*this == other); + } + + bool contains(const Vector3int16& p) const + { + return + static_cast(p.x - minPos.x) <= static_cast(maxPos.x - minPos.x) && + static_cast(p.y - minPos.y) <= static_cast(maxPos.y - minPos.y) && + static_cast(p.z - minPos.z) <= static_cast(maxPos.z - minPos.z); + } + + bool empty() const + { + return minPos.x > maxPos.x || minPos.y > maxPos.y || minPos.z > maxPos.z; + } + }; +} \ No newline at end of file diff --git a/App/util/Region3int32.h b/App/util/Region3int32.h new file mode 100644 index 0000000..9d2557a --- /dev/null +++ b/App/util/Region3int32.h @@ -0,0 +1,30 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ +#pragma once + +#include "Util/Vector3int32.h" + +namespace RBX { + + class Region3int32 { + private: + Vector3int32 minPos; + Vector3int32 maxPos; + + public: + Region3int32(); + Region3int32(const Vector3int32& min, const Vector3int32& max); + + ~Region3int32() {} + + Vector3int32 getMinPos() const; + Vector3int32 getMaxPos() const; + + inline bool operator==(const Region3int32& other) const { + return (minPos == other.minPos) && (maxPos == other.maxPos); + } + + inline bool operator!=(const Region3int32& other) const { + return !(*this == other); + } + }; +} \ No newline at end of file diff --git a/App/util/RobloxGoogleAnalytics.h b/App/util/RobloxGoogleAnalytics.h new file mode 100644 index 0000000..465873e --- /dev/null +++ b/App/util/RobloxGoogleAnalytics.h @@ -0,0 +1,86 @@ +/** + * RobloxGoogleAnalytics.h + * Copyright (c) 2013 ROBLOX Corp. All rights reserved. + */ + +#pragma once + +#include +#include + +#include + +namespace RBX { +// Singleton for tracking events across Studio using Google Analytics +// to process the data. Events are sent using the Measurement Protocol: +// https://developers.google.com/analytics/devguides/collection/protocol/v1 +// +// All events are posted to the server asynchronously and any calls to +// track data should return immediately. + +#define GA_CATEGORY_GAME "Game" +#define GA_CATEGORY_ACTION "Action" +#define GA_CATEGORY_ERROR "Error" +#define GA_CATEGORY_STUDIO "Studio" +#define GA_CATEGORY_COUNTERS "Counters" +#define GA_CATEGORY_RIBBONBAR "RibbonBar" +#define GA_CATEGORY_SECURITY "Security" +#define GA_CATEGORY_STUDIO_SETTINGS "StudioSettings" + +// timing variables +#define GA_CLIENT_START "ClientStartTime" + +namespace RobloxGoogleAnalytics +{ + static const std::string kGoogleAnalyticsBaseURL = "http://www.google-analytics.com/collect"; + + // Allow for easy initialization based on a lottery number. + // Calls setCanUseAnalytics and init. + void lotteryInit(const std::string &accountPropertyID, size_t maxThreadScheduleSize, int lotteryThreshold, const char * productName = NULL, int robloxAnalyticsLottery = -1, const std::string &sessionKey = "sessionID="); + + // Must be called before using the singleton. + void init(const std::string &accountPropertyID, size_t maxThreadScheduleSize, const char * productName = NULL); + + bool isInitialized(); + + bool getCanUseAnalytics(); + void setCanUseAnalytics(); + + void setUserID(int userID); + void setPlaceID(int placeID); + + // Signal sent on each call to track an analytic. + rbx::signal& analyticTrackedSignal(); + + void setExperimentVariation(const std::string& name, int value); + + void trackEvent( + const char *category, + const char *action = "custom", + const char *label = "none", + int value = 0, + bool sync = false); + + void trackEventWithoutThrottling( + const char *category, + const char *action = "custom", + const char *label = "none", + int value = 0, + bool sync = false); + + void trackUserTiming( + const char *category, + const char *variable, + int milliseconds, + const char *label = "none", + bool sync = false); + + void sendEventRoblox(const char* category, + const char* action = "custom", + const char* label = "none", + int value = 0, + bool sync = false); + + const std::string& getSessionId(); +} +} diff --git a/App/util/Rotation2d.h b/App/util/Rotation2d.h new file mode 100644 index 0000000..a0bfdbf --- /dev/null +++ b/App/util/Rotation2d.h @@ -0,0 +1,146 @@ +#pragma once + +#include "Util/G3DCore.h" + +namespace RBX { + +// Represents rotations in angles +class RotationAngle +{ +public: + RotationAngle() + : value(0) + , sin(0) + , cos(1) + { + } + + explicit RotationAngle(float angle) + { + float angleRad = angle * (G3D::pi() / 180.f); + + value = angle; + sin = sinf(angleRad); + cos = cosf(angleRad); + } + + bool empty() const + { + return value == 0.f; + } + + float getValue() const + { + return value; + } + + float getSin() const + { + return sin; + } + + float getCos() const + { + return cos; + } + + bool operator==(const RotationAngle& other) const + { + return value == other.value; + } + + bool operator!=(const RotationAngle& other) const + { + return value != other.value; + } + + RotationAngle inverse() const + { + RotationAngle result; + + result.value = -value; + result.sin = -sin; + result.cos = cos; + + return result; + } + + RotationAngle combine(const RotationAngle& other) const + { + RotationAngle result; + + result.value = value + other.value; + result.sin = sin * other.cos + cos * other.sin; + result.cos = cos * other.cos - sin * other.sin; + + return result; + } + +private: + float value; + float sin; + float cos; +}; + +class Rotation2D +{ +public: + Rotation2D() + { + } + + Rotation2D(const RotationAngle& angle, const Vector2& center) + : angle(angle) + , center(center) + { + } + + const RotationAngle& getAngle() const + { + return angle; + } + + const Vector2& getCenter() const + { + return center; + } + + bool empty() const + { + return angle.empty(); + } + + bool operator==(const Rotation2D& other) const + { + return angle == other.angle && center == other.center; + } + + bool operator!=(const Rotation2D& other) const + { + return angle != other.angle || center != other.center; + } + + Vector2 rotate(const Vector2& p) const + { + if (angle.empty()) + return p; + + Vector2 pl = p - center; + + return center + Vector2( + pl.x * angle.getCos() - pl.y * angle.getSin(), + pl.y * angle.getCos() + pl.x * angle.getSin()); + } + + Rotation2D inverse() const + { + return Rotation2D(angle.inverse(), center); + } + +private: + RotationAngle angle; + Vector2 center; + +}; + +} \ No newline at end of file diff --git a/App/util/RunStateOwner.h b/App/util/RunStateOwner.h new file mode 100644 index 0000000..e4aa8df --- /dev/null +++ b/App/util/RunStateOwner.h @@ -0,0 +1,170 @@ +#pragma once + +#include "rbx/TaskScheduler.h" +#include "v8Tree/Instance.h" +#include "v8Tree/Service.h" + +#include "boost/bind.hpp" +#include "boost/function.hpp" + +#include + +// TODO: Refactor: Move this out of Util +namespace RBX +{ + class DataModel; + class Region2; + + class Stepped + { + public: + const double gameTime; + const double gameStep; + const bool longStep; + Stepped(double gameTime, double gameStep, bool longStep):gameTime(gameTime),gameStep(gameStep),longStep(longStep) {} + }; + + class Heartbeat // This event occurs at regular intervals + { + public: + const double wallTime; + const double wallStep; + const double gameTime; + const double gameStep; + + const Time expirationTime; + Heartbeat(double wallTime, double wallStep, double gameTime, double gameStep, Time expirationTime):wallTime(wallTime),gameTime(gameTime),wallStep(wallStep),gameStep(gameStep),expirationTime(expirationTime) {} + }; + + // note - for now, in low to high precedence, for when used as events && multiple events + enum RunState { RS_STOPPED, + RS_RUNNING, + RS_PAUSED}; + + class RunTransition { + public: + RunState oldState; + RunState newState; + RunTransition(RunState oldState, RunState newState) + : oldState(oldState), newState(newState) + {} + }; + + extern const char* const sRunService; + +class PhysicsJob; +class HeartbeatTask; + +namespace Lua +{ + class WeakFunctionRef; +} + + class RunService + : public DescribedNonCreatable + , public Service + { + private: + typedef DescribedNonCreatable Super; + + typedef std::pair FunctionNameRefPair; + typedef std::map > EventCallbackMap; + + friend class PhysicsJob; + shared_ptr physicsJob; + friend class HeartbeatTask; + shared_ptr heartbeatTask; +#ifdef RBX_TEST_BUILD + friend class DummyTask; + std::vector > dummyTasks; +#else + std::vector > dummyTasksPadding; +#endif + + RunState runState; + double totalGameTime; // i.e. time in game + double totalGameTimeAtLastHeartbeat; + double totalWallTime; + double skippedTimeAccumulated; + + friend class DataModel; + + void stepDataModel(); + + EventCallbackMap renderSteppedEarlyCallbackMap; + public: + typedef enum + { + RENDERPRIORITY_FIRST = 0, + RENDERPRIORITY_INPUT = 100, + RENDERPRIORITY_CAMERA = 200, + RENDERPRIORITY_CHARACTER = 300, + RENDERPRIORITY_LAST = 2000, + } RenderPriority; + + RunService(); + ~RunService(); + + static bool parallelPhysicsUserEnabled; + + rbx::signal highPrioritySteppedSignal; + rbx::signal steppedSignal; + rbx::signal renderSteppedSignal; + rbx::signal earlyRenderSignal; + rbx::signal heartbeatSignal; + rbx::signal runTransitionSignal; + + TaskScheduler::Job* getPhysicsJob(); + TaskScheduler::Job* getHeartbeat(); + + rbx::signal scriptSteppedSignal; + rbx::signal scriptHeartbeatSignal; + rbx::signal scriptRenderSteppedSignal; + rbx::signal scriptRenderSteppedEarlySignal; + + rbx::signal* getOrCreateScriptRenderSteppedSignal(bool create = true); + + void bindFunctionToRenderStepEarly(std::string name, int priority, Lua::WeakFunctionRef functionToBind); + void unbindFunctionFromRenderStepEarly(std::string name); + + void fireRenderStepEarlyFunctions(); + + void setRunState(RunState newState); + void run() {setRunState(RS_RUNNING);} + void pause() {setRunState(RS_PAUSED);} + void stop() {setRunState(RS_STOPPED);} + + void stopTasks(); + void start(); + + void raiseHeartbeat(double step, const Time::Interval& stepBudget); + void gameStepped(double step, bool longStep); + void gameNotStepped(double skipped); + void renderStepped(double step, bool longStep); + + RunState getRunState() const {return runState;} + bool isEditState() const {return (runState == RS_STOPPED);} + bool isRunState() const {return (runState == RS_RUNNING);} + bool isPauseState() const {return (runState == RS_PAUSED);} + bool isRunning() {return (runState == RS_RUNNING);} + + bool isServer(); + bool isClient(); + bool isStudio(); + bool isRunMode(); + + ////////////////////////////////////////////////////// + // + double wallTime() const {return totalWallTime;} + double gameTime() const {return totalGameTime;} + double smoothFps() const; + double heartbeatFps() const; + double physicsCpuFraction() const; + double heartbeatCpuFraction() const; + double physicsAverageStep() const; + double heartbeatAverageStep() const; + protected: + void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); + }; + +} // namespace diff --git a/App/util/RunningAverage.h b/App/util/RunningAverage.h new file mode 100644 index 0000000..84ff89f --- /dev/null +++ b/App/util/RunningAverage.h @@ -0,0 +1,33 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/G3DCore.h" +#include "Util/Quaternion.h" + + +namespace RBX { + + class RunningAverageState + { + private: + Vector3 position; + Quaternion angles; + + static float weight(); // % of prior average to use + + public: + static int stepsToSleep(); // number of good steps to sleep + + RunningAverageState() {} + + void reset(const CoordinateFrame& cofm); + + void update(const CoordinateFrame& cofm, float radius); + + bool withinTolerance(const CoordinateFrame& cofm, float radius, float tolerance); + }; + + + +}// namespace diff --git a/App/util/ScopedAssign.h b/App/util/ScopedAssign.h new file mode 100644 index 0000000..7c1a287 --- /dev/null +++ b/App/util/ScopedAssign.h @@ -0,0 +1,33 @@ +#pragma once + +namespace RBX +{ + // TODO: This probably exists in some other library somewhere... + template + class ScopedAssign + { + V* value; + V oldValue; + public: + ScopedAssign() : value(NULL) {} + ScopedAssign(V& value, const V& newValue) + :value(&value) + ,oldValue(value) + { + *(this->value) = newValue; + } + ~ScopedAssign() + { + if (value) + *value = oldValue; + } + + void assign (V& value, const V& newValue) + { + this->value = &value; + oldValue = value; + *(this->value) = newValue; + } + }; +} + diff --git a/App/util/ScriptInformationProvider.h b/App/util/ScriptInformationProvider.h new file mode 100644 index 0000000..735c34c --- /dev/null +++ b/App/util/ScriptInformationProvider.h @@ -0,0 +1,42 @@ +/* Copyright 2003-2009 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include +#include +#include "rbx/boost.hpp" +#include "rbx/rbxTime.h" +#include "V8Tree/Service.h" +#include "Util/AsyncHttpCache.h" +#define BOOST_DATE_TIME_NO_LIB +#include "boost/date_time/posix_time/posix_time.hpp" +#include "Util/HeartbeatInstance.h" + +namespace RBX +{ + class Instance; + + extern const char* const sScriptInformationProvider; + class ScriptInformationProvider + : public DescribedNonCreatable + , public Service + { + private: + typedef DescribedNonCreatable Super; + + std::string assetUrl; + std::string access; + + public: + ScriptInformationProvider(); + + void setAssetUrl(std::string url) + { + assetUrl = url; + } + void setAccessKey(std::string access) + { + this->access = access; + } + }; +} //namespace \ No newline at end of file diff --git a/App/util/Selectable.h b/App/util/Selectable.h new file mode 100644 index 0000000..a0ce9c9 --- /dev/null +++ b/App/util/Selectable.h @@ -0,0 +1,15 @@ +/* + Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved +*/ + +#pragma once + +namespace RBX +{ + // Base class to control all selection functionality + class RBXBaseClass Selectable + { + public: + virtual bool isSelectable3d() { return true; } + }; +} // namespace diff --git a/App/util/Shared/Http.cpp b/App/util/Shared/Http.cpp index 1648c27..8e909e4 100644 --- a/App/util/Shared/Http.cpp +++ b/App/util/Shared/Http.cpp @@ -1081,12 +1081,12 @@ bool Http::isStrictlyRobloxSite(const char* url) { RBX::Url parsed = RBX::Url::fromString(url); - return parsed.isSubdomainOf("watrbx.wtf") || parsed.isSubdomainOf("pizzaboxer.fun"); + return parsed.isSubdomainOf("watrbx.wtf") || parsed.isSubdomainOf("robloxlabs.com"); } std::string host(HTParse(url, NULL, PARSE_HOST)); if ("watrbx.wtf" != host && !hasEnding(host, ".watrbx.wtf") - && "pizzaboxer.fun" != host && !hasEnding(host, ".pizzaboxer.fun")) + && "robloxlabs.com" != host && !hasEnding(host, ".robloxlabs.com")) { return false; } @@ -1119,7 +1119,7 @@ bool Http::isRobloxSite(const char* url) urlPath.MakeLower(); // trust urls from watrbx.wtf - if (hostName.Right(10)=="watrbx.wtf" || hostName.Right(14)=="pizzaboxer.fun") + if (hostName.Right(10)=="watrbx.wtf" || hostName.Right(14)=="robloxlabs.com") return true; // trust facebook login @@ -1159,7 +1159,7 @@ bool Http::isRobloxSite(const char* url) const bool isRoblox = parsed.isSubdomainOf("watrbx.wtf") || - parsed.isSubdomainOf("pizzaboxer.fun"); + parsed.isSubdomainOf("robloxlabs.com"); const bool isFacebook = ("login.facebook.com" == parsed.host() @@ -1206,7 +1206,7 @@ bool Http::isRobloxSite(const char* url) return "watrbx.wtf" == host || hasEnding(host, ".watrbx.wtf") || - "pizzaboxer.fun" == host || hasEnding(host, ".pizzaboxer.fun") || + "robloxlabs.com" == host || hasEnding(host, ".robloxlabs.com") || // trust facebook login ("login.facebook.com" == host && "/login.php") || ("ssl.facebook.com" == host && "/connect/uiserver.php" == path) || @@ -1240,7 +1240,7 @@ bool Http::isExternalRequest(const char* url) std::string hostname = urlParsed.GetHostName(); - if(hostname.find("watrbx.wtf") != std::string::npos || hostname.find("pizzaboxer.fun") != std::string::npos) + if(hostname.find("watrbx.wtf") != std::string::npos || hostname.find("robloxlabs.com") != std::string::npos) return false; return true; @@ -1256,7 +1256,7 @@ bool Http::isExternalRequest(const char* url) } return !parsed.isSubdomainOf("watrbx.wtf") - && !parsed.isSubdomainOf("pizzaboxer.fun"); + && !parsed.isSubdomainOf("robloxlabs.com"); } std::string host; @@ -1274,7 +1274,7 @@ bool Http::isExternalRequest(const char* url) return "watrbx.wtf" != host && !hasEnding(host, ".watrbx.wtf") && - "pizzaboxer.fun" != host && !hasEnding(host, ".pizzaboxer.fun"); + "robloxlabs.com" != host && !hasEnding(host, ".robloxlabs.com"); } void Http::setProxy(const std::string& host, long port) diff --git a/App/util/SimSendFilter.h b/App/util/SimSendFilter.h new file mode 100644 index 0000000..eefc14d --- /dev/null +++ b/App/util/SimSendFilter.h @@ -0,0 +1,23 @@ +/* Copyright 2003-2008 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/Region2.h" +#include "Util/SystemAddress.h" +#include + +namespace RBX { + + class SimSendFilter { + public: + typedef enum {EditVisit, Client, Server, dPhysClient, dPhysServer} Mode; + + Mode mode; + RBX::SystemAddress networkAddress; + Region2 region; + + SimSendFilter() : mode(Client) + {} + }; + +} // namespace diff --git a/App/util/Sound.h b/App/util/Sound.h new file mode 100644 index 0000000..a4afcdd --- /dev/null +++ b/App/util/Sound.h @@ -0,0 +1,58 @@ +/* Copyright 2003-2014 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "v8tree/Instance.h" +#include "Reflection/Event.h" + +#define FMOD_RESOURCES_FREED_STRING "FMOD System already closed. Resources previously freed." + +namespace FMOD +{ + class System; + class Sound; + class Channel; + class ChannelGroup; + class DSP; +} + +namespace RBX +{ + namespace Soundscape + { + // A wrapper of contentId we expose to lua as a type + class SoundId : public ContentId + { + public: + SoundId(const ContentId& id):ContentId(id) {} + SoundId(const char* id):ContentId(id) {} + SoundId(const std::string& id):ContentId(id) {} + SoundId() {} + }; + + // essentially a wrapper for FMOD::Sound with some extra info + class Sound : boost::noncopyable + { + FMOD::Sound* fmod_sound; + shared_ptr const system; + int refCount; // Sadly, we can't use shared_ptr logic to manage the lifetime of fmod_sound + bool isStreaming; + + public: + SoundId const id; + bool const is3D; + Sound(shared_ptr& system, SoundId id, bool is3D):fmod_sound(0),system(system),id(id),is3D(is3D),refCount(0),isStreaming(false) {} + ~Sound() { release(); } + FMOD::Sound* get() {return fmod_sound;} + FMOD::Sound* tryLoad(const RBX::Instance* context); + void detatch() { fmod_sound = 0; } + void release(); + + bool isReferenced() const { return refCount > 0; } + void acquire() { ++refCount; } + void unacquire() { refCount = std::max(0, refCount - 1); } + + bool getIsStreaming() const { return isStreaming; } + }; + } // namespace Soundscape +} // namespace RBX \ No newline at end of file diff --git a/App/util/SoundChannel.h b/App/util/SoundChannel.h new file mode 100644 index 0000000..13bc900 --- /dev/null +++ b/App/util/SoundChannel.h @@ -0,0 +1,171 @@ +/* Copyright 2003-2014 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "v8tree/Instance.h" +#include "Reflection/Event.h" +#include "Util/Sound.h" +#include "Util/RunStateOwner.h" + +namespace FMOD +{ + class Channel; +} + +struct FMOD_CHANNEL; + +namespace RBX { + + class PartInstance; + + extern void registerSound(); + + namespace Soundscape + { + enum RollOffMode + { + Inverse = 0, + Linear, + }; + // A simple sound object + extern const char* const sSoundChannel; + class SoundChannel + : public DescribedCreatable + , public Diagnostics::Countable + { + private: + typedef DescribedCreatable Super; + shared_ptr sound; + FMOD::Channel* fmod_channel; // the latest channel + + RBX::Timer lastTimePosReplication; // to regulate how often we replicate the time + + SoundId soundId; + float volume; + float pitch; + float minDistance; + float maxDistance; + RollOffMode rollOff; + float defaultFrequency; + + double soundPositionSeconds; + + int numOfTimesLooped; + unsigned lastSoundPositionMsec; + + bool playOnRemove; + bool is3D : 1; + bool looped : 1; + bool soundDisabled : 1; // a cached value coming from SoundService + int playCount; // actual number of how many times this sound has played + int reqPlayCount; // requested number of play calls. Hack to get play() and pause() to replicate (-1 stopped, 0 paused, 1+ play) + mutable bool invalidChannel : 1; + + PartInstance* part; // The Part (if any) that this sound is attached to + + rbx::signals::scoped_connection serverUpdatedTimeConnection; + rbx::signals::scoped_connection serverScriptUpdatedTimeConnection; + rbx::signals::scoped_connection serverResumedSoundConnection; + + /** + * Some sounds are "played" everywhere, but should only be heard by certain peers. + * For example, sounds in Player GUIs should only be heard by the Player that sees the GUI. + **/ + bool isHeardLocally(const Instance* context) const; + // see if everyone in the world can hear this sound + bool isHeardGlobally() const; + + void updateLooped(); + void update3D(FMOD::Channel* channel); + void playLocal(const Instance *context); + void playSound(bool isResuming = false); + void playSound(const Instance* context, bool isResuming = false); + void releaseChannel(); + void loadSound(const Instance *context, bool shouldPlayOnLoad); + + void serverUpdatedTimePositionFromScript(unsigned int timePosition); + void serverUpdatedTimePosition(unsigned int timePosition); + + bool controlledByAndIsServer() const; + + protected: + /*override*/ bool askSetParent(const Instance* instance) const; + /*override*/ void onAncestorChanged(const AncestorChanged& event); + /*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); + + public: + rbx::signal soundLoopedSignal; + rbx::signal soundPausedSignal; + rbx::signal soundStoppedSignal; + rbx::signal soundPlayedSignal; + rbx::signal soundEndedSignal; + + rbx::remote_signal timePositionUpdatedFromServerSignal; + rbx::remote_signal timePositionUpdatedFromServerScriptSignal; + + rbx::remote_signal soundResumedFromServerSignal; + + SoundChannel(); + ~SoundChannel(); + + static Reflection::BoundProp sound_desc_playOnRemove; + + bool doFmodChannelAddressesMatch(const FMOD::Channel *channel) const; + + FMOD::Channel* getFMODChannel() { return fmod_channel; } + + void setSoundId(SoundId value); + const SoundId &getSoundId() const; + + float getVolume() const; + void setVolume(float value); + + float getPitch() const; + void setPitch(float value); + + float getMinDistance() const; + void setMinDistance(float value); + float getMaxDistance() const; + void setMaxDistance(float value); + + RollOffMode getRollOffMode() const { return rollOff; } + void setRollOffMode(Soundscape::RollOffMode value); + + bool getLooped() const; + void setLooped(bool value); + + int getPlayCount() const { return playCount; } + void setPlayCount(int value); + + void resume(); + void play(); + void pause(); + void stop(); + + // does not replicate play count + void playLocal(); + void pauseLocal(); + //// + + bool isPlaying() const; + bool isPaused() const; + bool isSoundLoaded() const; + + double getSoundLength() const; + + void setSoundPosition(double position, bool setFromLua = false); + void setSoundPositionLua(double position); + double getSoundPosition() const; + + bool getHasPlayed() const; + void setHasPlayed(bool value); + + void updateListenState(const Time::Interval& timeSinceLastStep); + + static void soundEnded(weak_ptr channelWeak, std::string soundId); + void onChannelEnd(const FMOD_CHANNEL *channel); + void onSoundLoaded(const Instance *context, bool shouldPlayOnLoad); + }; + + } // namespace Soundscape +} // namespace RBX diff --git a/App/util/SoundService.h b/App/util/SoundService.h new file mode 100644 index 0000000..5cb9598 --- /dev/null +++ b/App/util/SoundService.h @@ -0,0 +1,290 @@ +/* Copyright 2003-2014 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Tree/Service.h" +#include "V8datamodel/DataModel.h" +#include "V8DataModel/Stats.h" +#include "Util/SoundWorld.h" +#include "Util/SoundChannel.h" +#include "Reflection/Event.h" +#include "Util/IHasLocation.h" + +#include "fmod.h" +#include "fmod.hpp" +#include "fmod_errors.h" + + + +#if FMOD_VERSION != 0x00010702 +# error Wrong version of fmod. +#endif + + +namespace RBX +{ + typedef enum + { + FADE_STATUS_NONE = 0, + FADE_STATUS_IN, + FADE_STATUS_OUT + } FadeStatus; + + namespace Soundscape + { + class SoundJob; + class SoundChannel; + class Sound; + class SoundId; + + enum ReverbType + { + NoReverb = 0, + GenericReverb, + PaddedCell, + Room, + Bathroom, + LivingRoom, + StoneRoom, + Auditorium, + ConcertHall, + Cave, + Arena, + Hangar, + CarpettedHallway, + Hallway, + StoneCorridor, + Alley, + Forest, + City, + Mountains, + Quarry, + Plain, + ParkingLot, + SewerPipe, + UnderWater + }; + + enum ListenerType + { + CameraListener = 0, + CFrame, + ObjectPosition, + ObjectCFrame + }; + + struct listenerValues + { + CoordinateFrame listenCFrame; + shared_ptr listenObject; + }; + + extern const char* const sSoundService; + + class SoundService + : public DescribedCreatable + , public Service + { + private: + typedef DescribedCreatable Super; + typedef boost::unordered_set SoundChannels; + friend class SoundChannel; + shared_ptr system; + typedef boost::unordered_map > StockSounds; + StockSounds stockSounds; + float dopplerscale; + float distancefactor; + float rolloffscale; + ListenerType currentListenerType; + listenerValues currentListenerValues; + shared_ptr statsItem; + ReverbType ambientReverb; + SoundChannels soundChannels; + FMOD::ChannelGroup *channelMaster; + + shared_ptr soundJob; + + rbx::signals::scoped_connection gameSettingsChangedConnection; + + Time nextGarbageCollectTime; + typedef boost::unordered_map > LoadedSounds; + LoadedSounds loadedSounds; + LoadedSounds loaded3DSounds; + + float masterChannelFadeTimeMsec; + FadeStatus masterChannelFadeStatus; + + bool initialized; + bool muted; + + void openFmod(); + void closeFmod(); + void garbageCollectSounds(); + static void gcSounds(LoadedSounds& sounds); + static void getSoundStats(const LoadedSounds& sounds, unsigned int& numSounds, unsigned int& numUnusedSounds); + void updateSoundChannels(const Time::Interval& timeSinceLastStep); + void updateMasterChannelGroup(const Time::Interval& timeSinceLastStep); + + void update3DSettings(); + void on3DSettingChanged(const Reflection::PropertyDescriptor&) { update3DSettings(); } + void updateAmbientReverb(); + + protected: + /////////////////////////////////////////////////////////////////////////////////////////////// + // Instance Overrides + ////////////////////////////////////////////////////////////////////////////// + /*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); + + public: + static bool soundDisabled; // Used to suppress all fmod calls by builds that don't play sound (like web service) + + shared_ptr loadSound(SoundId id, bool is3D); + + bool enabled() const { return initialized; } + SoundService(); + ~SoundService(); + void playSound(SoundType sound); + + FMOD::DSP* createDSP(FMOD_DSP_DESCRIPTION &dspdesc); + int getSampleRate(); + + unsigned int getfmod_version() const; + void getSoundStats(unsigned int& numSounds, unsigned int& numUnusedSounds) const; + void getChannelsPlaying(int& value) const; + void muteAllChannels(bool mute); + bool isMuted(); + + void setListener(ListenerType listenerType, shared_ptr value); + shared_ptr getListener(); + CoordinateFrame getListenCFrame(Camera* camera); + void setMasterVolume(float value); + float getMasterVolume(); + + void setMasterVolumeFadeOut(float timeToFadeMsec); + void setMasterVolumeFadeIn(float timeToFadeMsec); + + void gameSettingsChanged(const Reflection::PropertyDescriptor* propertyDescriptor); + + FMOD::ChannelGroup* getMasterChannel() { return channelMaster; } + + struct CpuStats + { + float total; + float dsp; + float stream; + float geometry; + float update; + }; + void getCpuStats(CpuStats& stats) const; + + ReverbType getAmbientReverb() const { return ambientReverb; } + void setAmbientReverb(const ReverbType& value); + + static Reflection::BoundProp prop_dopplerscale; + static Reflection::BoundProp prop_distancefactor; + static Reflection::BoundProp prop_rolloffscale; + static Reflection::EnumPropDescriptor prop_AmbientReverb; + + void registerSoundChannel(SoundChannel *soundChannel); + void unregisterSoundChannel(SoundChannel *soundChannel); + + void step(const Time::Interval& timeSinceLastStep); + + static void checkResultNoThrow(FMOD_RESULT result, const char* fmodOperation, const void *rbxFmodParent, const void *fmodObject); + static void checkResult(FMOD_RESULT result, const char* fmodOperation, const void *rbxFmodParent, const void *fmodObject); + + static bool convert(const G3D::Vector3& src, FMOD_VECTOR& dst); + }; + + + // Responsible for updating all sound logic + class SoundJob : public DataModelJob + { + private: + SoundService* const soundService; + const double fps; + public: + SoundJob(SoundService* soundService) + :DataModelJob("Sound", DataModelJob::Write, false, + shared_from_dynamic_cast(DataModel::get(soundService)), Time::Interval(0.003)) + ,fps(30) + ,soundService(soundService) + { + cyclicExecutive = true; + } + + Time::Interval sleepTime(const Stats& stats) + { + return computeStandardSleepTime(stats, fps); + } + + virtual Job::Error error(const Stats& stats) + { + return computeStandardErrorCyclicExecutiveSleeping(stats, fps); + } + + TaskScheduler::StepResult stepDataModelJob(const Stats& stats) + { + soundService->step(stats.timespanSinceLastStep); + + return TaskScheduler::Stepped; + } + }; + + class SoundServiceStatsItem : public Stats::Item + { + const SoundService* service; + size_t currentalloced; + size_t maxalloced; + unsigned int numSounds; + unsigned int numUnusedSounds; + int channelsPlaying; + SoundService::CpuStats cpuStats; + public: + SoundServiceStatsItem(const SoundService* service) + :service(service),currentalloced(0),maxalloced(0) + { + setName("Sound"); + } + + static shared_ptr create(const SoundService* service) + { + shared_ptr result = Creatable::create(service); + Stats::Item* cpu = result->createBoundPercentChildItem("CPU", result->cpuStats.total); + cpu->createBoundPercentChildItem("Dsp", result->cpuStats.dsp); + cpu->createBoundPercentChildItem("Stream", result->cpuStats.stream); + cpu->createBoundPercentChildItem("Geometry", result->cpuStats.geometry); + cpu->createBoundPercentChildItem("Update", result->cpuStats.update); + result->createBoundChildItem("ChannelsPlaying", result->channelsPlaying); + result->createBoundMemChildItem("Current", result->currentalloced); + result->createBoundMemChildItem("Max", result->maxalloced); + result->createBoundChildItem("# Sounds", result->numSounds); + result->createBoundChildItem("# Unused", result->numUnusedSounds); + return result; + } + + /*override*/ void update() + { + if (service->enabled()) + { + this->formatValue(service->getfmod_version(), "fmod %08x", service->getfmod_version()); + int a, b; + if (FMOD::Memory_GetStats(&a, &b)==FMOD_OK) + { + currentalloced = (size_t) a; + maxalloced = (size_t) b; + } + service->getSoundStats(numSounds, numUnusedSounds); + service->getChannelsPlaying(channelsPlaying); + service->getCpuStats(cpuStats); + } + else + { + this->setValue(0, "-disabled-"); + } + } + }; + + + } // namespace Soundscape +} // namespace RBX \ No newline at end of file diff --git a/App/util/SoundWorld.h b/App/util/SoundWorld.h new file mode 100644 index 0000000..a614c61 --- /dev/null +++ b/App/util/SoundWorld.h @@ -0,0 +1,39 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +namespace RBX +{ + typedef enum SoundType { NO_SOUND = 0, + BOING_SOUND, + BOMB_SOUND, + BREAK_SOUND, + CLICK_SOUND, + CLOCK_SOUND, + RUBBERBAND_SOUND, + PAGE_SOUND, + PING_SOUND, + SNAP_SOUND, + SPLAT_SOUND, + STEP_SOUND, + STEP_ON_SOUND, + SWOOSH_SOUND, + VICTORY_SOUND + } SoundType; + + + class SoundWorld { + public: + + static SoundType ActionSound() {return PING_SOUND;} + static SoundType TrashSound() {return PAGE_SOUND;} + static SoundType ClickSound() {return CLICK_SOUND;} + static SoundType SplatSound() {return SPLAT_SOUND;} + static SoundType StepSound() {return STEP_SOUND;} + static SoundType StepOnSound() {return STEP_ON_SOUND;} + static SoundType SwooshSound() {return SWOOSH_SOUND;} + static SoundType LimitSound() {return BOING_SOUND;} + static SoundType WinSound() {return VICTORY_SOUND;} + static SoundType LoseSound() {return BOING_SOUND;} + }; +} diff --git a/App/util/SpanningEdge.h b/App/util/SpanningEdge.h new file mode 100644 index 0000000..0cfcb7e --- /dev/null +++ b/App/util/SpanningEdge.h @@ -0,0 +1,62 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/IndexedTree.h" + +namespace RBX { + + class SpanningNode; + class SpanningTree; + + class SpanningEdge + { + friend class SpanningTree; + + private: + void removeFromSpanningTree(); + void addToSpanningTree(SpanningNode* newParent); + + protected: + + public: + SpanningEdge() {} + + virtual ~SpanningEdge() {} + + bool isLighterThan(const SpanningEdge* other) const { + return other->isHeavierThan(this); + } + + bool inSpanningTree() const; + + SpanningNode* getChildSpanningNode(); + SpanningNode* getParentSpanningNode(); + + const SpanningNode* getConstChildSpanningNode() const; + const SpanningNode* getConstParentSpanningNode() const; + + ////////////////////////////////////////////////////////// + // + virtual bool isHeavierThan(const SpanningEdge* other) const = 0; + + virtual SpanningNode* otherNode(SpanningNode* n) = 0; + virtual const SpanningNode* otherConstNode(const SpanningNode* n) const = 0; + + virtual SpanningNode* getNode(int i) = 0; + virtual const SpanningNode* getConstNode(int i) const = 0; + + SpanningNode* otherNode(int i) { + RBXASSERT_VERY_FAST((i == 0) || (i == 1)); + return getNode((i + 1) % 2); + } + + ////////////////////////////////////////////////////////// + // + static bool heavierEdge(const SpanningEdge* test, const SpanningEdge* other) { + return test->isHeavierThan(other); + } + }; + +} // namespace + diff --git a/App/util/SpanningNode.h b/App/util/SpanningNode.h new file mode 100644 index 0000000..bb82a62 --- /dev/null +++ b/App/util/SpanningNode.h @@ -0,0 +1,62 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/IndexedMesh.h" + +namespace RBX { + + class SpanningEdge; + + class SpanningNode : public IndexedMesh + { + friend class SpanningEdge; + + private: + SpanningEdge* edgeToParent; + + protected: + virtual SpanningEdge* getFirstSpanningEdge() = 0; + virtual SpanningEdge* getNextSpanningEdge(SpanningEdge* edge) = 0; + + void setEdgeToParent(SpanningEdge* edge); + + public: + SpanningNode() : edgeToParent(NULL) {} + + ~SpanningNode() {} + + SpanningNode* getParent() {return getTypedParent();} + const SpanningNode* getConstParent() const {return getConstTypedParent();} + + SpanningNode* getChild(int i) {return getTypedChild(i);} + + SpanningEdge* getEdgeToParent() {return edgeToParent;} + const SpanningEdge* getConstEdgeToParent() const {return edgeToParent;} + + static int getDepth(SpanningNode* node) { + if (!node) { + return 0; + } + else if (!node->getParent()) { + return 1; + } + else { + return getDepth(node->getParent()) + 1; + } + } + + bool lessThan(const IndexedTree* other) const; + + template + inline void visitEdges(Func func) + { + SpanningEdge* edge = getFirstSpanningEdge(); + while (edge) { + func(this, edge); + edge = getNextSpanningEdge(edge); + } + } + }; + +} // namespace diff --git a/App/util/SpanningTree.h b/App/util/SpanningTree.h new file mode 100644 index 0000000..0131bcf --- /dev/null +++ b/App/util/SpanningTree.h @@ -0,0 +1,53 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "G3D/Array.h" +#include + +namespace RBX { + + class SpanningNode; + class SpanningEdge; + + class SpanningTree + { + private: + G3D::Array tempEdges; + int size; + + static SpanningNode* lightParent(int testSide, SpanningNode* child, SpanningEdge*& answer, int& lightSide); + static SpanningNode* testEdgeToParent(int testSide, SpanningNode* child, SpanningEdge*& answer, int& lightSide); + static void findLightestUpstream(SpanningNode* n0, SpanningNode* n1, int d0, int d1, SpanningEdge*& answer, int& lightSide); + static void buildDownstreamTree(SpanningNode* root, std::set& tree); + + void removeEdge(SpanningEdge* edge); + void addEdge(SpanningEdge* edge, SpanningNode* newParent); + + void findAndDeactivateEdges(SpanningNode* child, SpanningEdge* deactivate, G3D::Array& toActivate); + void activateEdges(SpanningNode* child, const G3D::Array& toActivate); + + static void findLightestUpstream(SpanningEdge* e, SpanningEdge*& answer, int& lightSide); + static SpanningEdge* findHeaviestDownstream(SpanningNode* node, SpanningNode*& newParent); + + void swapTree(SpanningEdge* deactivate, SpanningEdge* activate, SpanningNode* newParent); + void swap(SpanningEdge* deactivate, SpanningEdge* activate, SpanningNode* newParent); + + + protected: + /*implement*/ virtual void onSpanningEdgeAdding(SpanningEdge* edge, SpanningNode* child) {} + /*implement*/ virtual void onSpanningEdgeAdded(SpanningEdge* edge) {} + /*implement*/ virtual void onSpanningEdgeRemoving(SpanningEdge* edge) {} + /*implement*/ virtual void onSpanningEdgeRemoved(SpanningEdge* edge, SpanningNode* child) {} + /*implement*/ virtual bool validateTree(SpanningNode* root) {return true;} + + public: + void insertSpanningTreeEdge(SpanningEdge* insertEdge); + void removeSpanningTreeEdge(SpanningEdge* removeEdge); + + SpanningTree(); + ~SpanningTree(); + }; + +} // namespace + diff --git a/App/util/SpatialRegion.h b/App/util/SpatialRegion.h new file mode 100644 index 0000000..0d9e88c --- /dev/null +++ b/App/util/SpatialRegion.h @@ -0,0 +1,128 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "Util/Region3int16.h" +#include "Util/Vector3int32.h" + +namespace RBX { + +// Utilities for working with sub-sections of global coordinate space called +// SpacialRegions. The sub-sections are of a fixed, globally defined size and +// position. The sub-sections are integer aligned and origin aligned (the +// origin is at the meeting point of 8 regions). +namespace SpatialRegion { + +namespace Constants { + // these are const int to allow proper constant folding, inlining, and + // to allow other, derived static const int values to also have these + // benefits. + const int kRegionXDimensionInVoxelsAsBitShift = 5; + const int kRegionYDimensionInVoxelsAsBitShift = 4; + const int kRegionZDimensionInVoxelsAsBitShift = 5; + + const int kRegionInVoxelsAsBitShift = kRegionXDimensionInVoxelsAsBitShift + kRegionYDimensionInVoxelsAsBitShift + kRegionZDimensionInVoxelsAsBitShift; + + const int kRegionXDimensionInVoxels = 1 << Constants::kRegionXDimensionInVoxelsAsBitShift; + const int kRegionYDimensionInVoxels = 1 << Constants::kRegionYDimensionInVoxelsAsBitShift; + const int kRegionZDimensionInVoxels = 1 << Constants::kRegionZDimensionInVoxelsAsBitShift; + + const int kMaxXVoxelOffsetInsideRegion = kRegionXDimensionInVoxels - 1; + const int kMaxYVoxelOffsetInsideRegion = kRegionYDimensionInVoxels - 1; + const int kMaxZVoxelOffsetInsideRegion = kRegionZDimensionInVoxels - 1; +} + +namespace _PrivateConstants { + extern const Vector3int16 kRegionDimensionInStudsAsBitShifts; +} + +// Identifier object for regions of space. Has a Vector3int16 representation +// also. The Vector3int16 representation has constraints: +// * If two Ids represent the same area of space, then the Vector3int16's are == +// * If one Vector3int16's values (x, y, and/or z) is > another, then the +// spacial coordinates on those axis(es) are also >. +// * If two regions are adjacent, then the Vector3int16 values will be +// sequential on the axis(es) they are adjacent in. +// * The Vector3int16 representation is consitent between runs, and between +// client/server. +class Id { + Vector3int16 internalValue; +public: + explicit Id(const Vector3int16& internalValue) : internalValue(internalValue) {} + Id(int x, int y, int z) : internalValue(Vector3int16(x,y,z)) {} + + const Vector3int16& value() const { return internalValue; } + + Id operator+(const Vector3int16& other) const { return Id(internalValue + other); } + bool operator==(const Id& other) const { return internalValue == other.internalValue; } + bool operator!=(const Id& other) const { return internalValue != other.internalValue; } + + struct boost_compatible_hash_value { + size_t operator()(const Id& key) const { + return Vector3int16::boost_compatible_hash_value()(key.internalValue); + } + }; +}; + +std::size_t hash_value(const Id& key); + +inline Vector3int16 getRegionDimensionsInVoxels() { + using namespace Constants; + return Vector3int16( + kRegionXDimensionInVoxels, + kRegionYDimensionInVoxels, + kRegionZDimensionInVoxels); +} + +inline Vector3int16 getRegionDimensionInVoxelsAsBitShifts() { + using namespace Constants; + return Vector3int16( + kRegionXDimensionInVoxelsAsBitShift, + kRegionYDimensionInVoxelsAsBitShift, + kRegionZDimensionInVoxelsAsBitShift); +} + +inline Vector3int16 getMaxVoxelOffsetInsideRegion() { + using namespace Constants; + return Vector3int16( + kMaxXVoxelOffsetInsideRegion, + kMaxYVoxelOffsetInsideRegion, + kMaxZVoxelOffsetInsideRegion); +} + +inline Id regionContainingVoxel(const Vector3int16& globalVoxelCoordinate) { + return Id(globalVoxelCoordinate >> getRegionDimensionInVoxelsAsBitShifts()); +} + +inline Vector3int16 voxelCoordinateRelativeToEnclosingRegion( + const Vector3int16& globalVoxelCoordinate) { + return globalVoxelCoordinate & getMaxVoxelOffsetInsideRegion(); +} + +inline Vector3int16 globalVoxelCoordinateFromRegionAndRelativeCoordinate( + const Id& id, const Vector3int16& relativeCoordinate) { + return (id.value() << getRegionDimensionInVoxelsAsBitShifts()) + relativeCoordinate; +} + +inline Region3int16 inclusiveVoxelExtentsOfRegion(const Id& id) { + Vector3int16 min(id.value() << getRegionDimensionInVoxelsAsBitShifts()); + return Region3int16(min, min + getMaxVoxelOffsetInsideRegion()); +} + +inline Vector3int32 smallestCornerOfRegionInGlobalCoordStuds(const Id& id) { + return Vector3int32(id.value()) << _PrivateConstants::kRegionDimensionInStudsAsBitShifts; +} + +inline Vector3int32 centerOfRegionInGlobalCoordStuds(const Id& id) { + static const Vector3int32 kHalfDimensionInStuds(Vector3int32::one() << + (_PrivateConstants::kRegionDimensionInStudsAsBitShifts - Vector3int16::one())); + return smallestCornerOfRegionInGlobalCoordStuds(id) + + kHalfDimensionInStuds; +} + +inline Vector3int32 largestCornerOfRegionInGlobalCoordStuds(const Id& id) { + return smallestCornerOfRegionInGlobalCoordStuds(Id(id.value() + Vector3int16::one())); +} + +} // SpacialRegion + +} // RBX diff --git a/App/util/Statistics.h b/App/util/Statistics.h new file mode 100644 index 0000000..5891306 --- /dev/null +++ b/App/util/Statistics.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include "Util/HttpAsync.h" + +class SimpleJSON; + +#ifdef RBX_TEST_BUILD +void SetDefaultFilePath(const std::string &path); +const std::string &GetDefaultFilePath(); +#endif + +void SetBaseURL(const std::string& baseUrl); +const std::string& GetBaseURL(); + +void ReportStatisticWithMessage(const std::string& baseUrl, const std::string& id, + const std::string& simpleMessage, + const char* secondaryFilterName = NULL, const char* secondaryFilterValue = NULL); + +void ReportStatistic(const std::string& baseUrl, const std::string& id, + const std::string& primaryFilterName, const std::string& primaryFilterValue, + const std::string& secondaryFilterName, const std::string& secondaryFilterValue); + +void ReportStatisticPost(const std::string& baseUrl, const std::string& id, const std::string& postData, + const char* secondaryFilterName, const char* secondaryFilterValue); + + +std::string UploadLogFile(const std::string& baseUrl, const std::string& data); + +bool FetchLocalClientSettingsData(const char* group, SimpleJSON* dest); +void LoadClientSettingsFromString(const char* group, const std::string& settingsData, SimpleJSON* dest); +bool FetchClientSettingsData(const char* group, const char* apiKey, SimpleJSON* dest); +void FetchClientSettingsData(const char* group, const char* apiKey, std::string* dest); +RBX::HttpFuture FetchClientSettingsDataAsync(const char* group, const char* apiKey); +RBX::HttpFuture FetchABTestDataAsync(const std::string& url); +std::string LoadABTestFromString(const std::string& responseData); \ No newline at end of file diff --git a/App/util/SteppedInstance.h b/App/util/SteppedInstance.h new file mode 100644 index 0000000..09479ea --- /dev/null +++ b/App/util/SteppedInstance.h @@ -0,0 +1,41 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Tree/Instance.h" +#include "Util/RunStateOwner.h" +#include "Util/G3DCore.h" + +LOGGROUP(ISteppedLifetime) + +namespace RBX { + + class IStepped + { + public: + enum StepType + { + StepType_Default, + StepType_HighPriority, + StepType_Render, + }; + + private: + StepType stepType; + rbx::signals::scoped_connection steppedConnection; + + protected: + // call this inside onServiceProvider + void onServiceProviderIStepped(ServiceProvider* oldProvider, ServiceProvider* newProvider); + + /*implement*/ virtual void onStepped(const Stepped& event) = 0; + + void stopStepping() { + steppedConnection.disconnect(); + } + + public: + IStepped(StepType stepType = StepType_Default): stepType(stepType) {} + virtual ~IStepped() {} + }; +} // namespace RBX \ No newline at end of file diff --git a/App/util/StlExtra.h b/App/util/StlExtra.h new file mode 100644 index 0000000..b284eda --- /dev/null +++ b/App/util/StlExtra.h @@ -0,0 +1,73 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "rbx/Debug.h" +#include + + +namespace RBX { + + // only fast for very short vectors - should do no allocation + // returns index of the item removed + + template + void fastRemoveIndex(std::vector& vec, size_t index) + { + RBXASSERT(index >= 0); + RBXASSERT(index < vec.size()); + RBXASSERT(!vec.empty()); + RBXASSERT(vec.size() < 32); // Note - possibly should be using some other container here - to find item requires N time +#ifdef _DEBUG + size_t currentCapacity = vec.capacity(); +#endif + size_t newSize = vec.size() - 1; + + if (index < newSize) + { + vec[index] = vec.back(); + } + vec.resize(newSize); // hopefully, don't do a memory realloc/shrink + +#ifdef _DEBUG + RBXASSERT(currentCapacity == vec.capacity()); // confirm no reallocation +#endif + } + + + // only fast for very short vectors - should do no allocation + // returns index of the item removed + template + size_t fastRemoveShort(std::vector& vec, const T& item) + { + typename std::vector::iterator it = std::find(vec.begin(), vec.end(), item); + + size_t answer = it - vec.begin(); + + RBXASSERT(vec[answer] == item); + RBXASSERT(it != vec.end()); + RBXASSERT(vec.size() < 32); // Note - possibly should be using some other container here - to find item requires N time +#ifdef _DEBUG + size_t currentCapacity = vec.capacity(); +#endif + + typename std::vector::iterator lastOne(vec.end()); + --lastOne; + + RBXASSERT(*lastOne == vec.back()); + + if (it != lastOne) { + *it = *lastOne; // move back item into the place once held by item + } + vec.resize(vec.size() - 1); // hopefully, don't do a memory realloc/shrink + +#ifdef _DEBUG + RBXASSERT(currentCapacity == vec.capacity()); // confirm no reallocation +#endif + return answer; + } + + + +} // namespace RBX + diff --git a/App/util/StreamRegion.h b/App/util/StreamRegion.h new file mode 100644 index 0000000..5ed058d --- /dev/null +++ b/App/util/StreamRegion.h @@ -0,0 +1,239 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "Util/Region3int32.h" +#include "Util/Vector3int32.h" +#include "v8world/ContactManagerSpatialHash.h" +#include "Util/Extents.h" +#include "Voxel/Util.h" + +namespace RBX { + + // Utilities for working with sub-sections of global coordinate space called + // StreamRegions. The sub-sections are of a fixed, globally defined size and + // position. The sub-sections are integer aligned and origin aligned (the + // origin is at the meeting point of 8 regions). + namespace StreamRegion { + + namespace Constants { + // these are const int to allow proper constant folding, inlining, and + // to allow other, derived static const int values to also have these + // benefits. + const int kMinNumPlayableRegion = 3*3*3; + } + namespace _PrivateConstants { + const int kRegionSizeInVoxelsAsBitShift = 4; + const int kRegionSizeInStudsAsBitShift = + kRegionSizeInVoxelsAsBitShift + Voxel::kCELL_SIZE_AS_BIT_SHIFT; + + const Vector3int32 kRegionDimensionInVoxelsAsBitShifts( + kRegionSizeInVoxelsAsBitShift, kRegionSizeInVoxelsAsBitShift, kRegionSizeInVoxelsAsBitShift); + + const Vector3int32 kRegionDimensionInStudsAsBitShifts( + kRegionSizeInStudsAsBitShift, kRegionSizeInStudsAsBitShift, kRegionSizeInStudsAsBitShift); + + const int kRegionDimensionInVoxels = 1 << kRegionSizeInVoxelsAsBitShift; + const int kMaxVoxelOffsetInsideRegion = kRegionDimensionInVoxels - 1; + } + + // Identifier object for regions of space. Has a Vector3int32 representation + // also. The Vector3int32 representation has constraints: + // * If two Ids represent the same area of space, then the Vector3int32's are == + // * If one Vector3int32's values (x, y, and/or z) is > another, then the + // spacial coordinates on those axis(es) are also >. + // * If two regions are adjacent, then the Vector3int32 values will be + // sequential on the axis(es) they are adjacent in. + // * The Vector3int32 representation is consistent between runs, and between + // client/server. + class Id { + Vector3int32 internalValue; + public: + Id() : internalValue(Vector3int32(0,0,0)) {} + explicit Id(const Vector3int32& internalValue) : internalValue(internalValue) {} + Id(int x, int y, int z) : internalValue(Vector3int32(x,y,z)) {} + + const Vector3int32& value() const { return internalValue; } + + Id operator+(const Vector3int32& other) const { return Id(internalValue + other); } + Id operator+(const Id &other) const { return Id(internalValue + other.internalValue); } + bool operator==(const Id& other) const { return internalValue == other.internalValue; } + bool operator!=(const Id& other) const { return internalValue != other.internalValue; } + + static int streamGridCellSizeInStuds() + { + return static_cast(SpatialHashStatic::hashGridSize(CONTACTMANAGER_MAXLEVELS-1)); + } + + static int getRegionLongestAxisDistance(Id r1, Id r2) + { + int xDistance = abs(r1.value().x - r2.value().x); + int yDistance = abs(r1.value().y - r2.value().y); + int zDistance = abs(r1.value().z - r2.value().z); + int result = xDistance; + if (yDistance > result) + { + result = yDistance; + } + if (zDistance > result) + { + result = zDistance; + } + return result; + } + + bool isRegionInTerrainBoundaries() const { + Region3int16 extents = Voxel::getTerrainExtentsInCells(); + Vector3int16 min = extents.getMinPos() >> _PrivateConstants::kRegionSizeInVoxelsAsBitShift; + Vector3int16 max = extents.getMaxPos() >> _PrivateConstants::kRegionSizeInVoxelsAsBitShift; + + const Vector3int32 &vv = value(); + return vv.x >= min.x && vv.y >= min.y && vv.z >= min.z && vv.x <= max.x && vv.y <= max.y && vv.z <= max.z; + } + + struct boost_compatible_hash_value { + size_t operator()(const Id& key) const { + const Vector3int32 &v = key.internalValue; + return (v.x * 11) + ((v.y * 7) << 10) + ((v.z * 3) << 20); + } + }; + }; + + std::size_t hash_value(const Id& key); + + class IdExtents { + public: + // inclusive stream region extents + Id low, high; + + bool operator==(const IdExtents& other) const { + return (low == other.low) && (high == other.high); + } + + // Determines if these extents intersect the regions in the + // argument container. If optionalFoundId is not null, it will + // be set to an example of an Id that is both inside these extents + // and inside the container. + template + bool intersectsContainer(const Container& container, Id* optionalFoundId = NULL) const { + const Vector3int32& extentsMin = low.value(); + const Vector3int32& extentsMax = high.value(); + Vector3int32 counter; + for (counter.y = extentsMin.y; counter.y <= extentsMax.y; counter.y++) { + for (counter.z = extentsMin.z; counter.z <= extentsMax.z; counter.z++) { + for (counter.x = extentsMin.x; counter.x <= extentsMax.x; counter.x++) { + Id counterId(counter); + if (container.find(counterId) != container.cend()) { + if (optionalFoundId) { + (*optionalFoundId) = counterId; + } + return true; + } + } + } + } + return false; + } + }; + + inline const Vector3int32& gridCellDimension() + { + static Vector3int32 v = Vector3int32(StreamRegion::Id::streamGridCellSizeInStuds(), + StreamRegion::Id::streamGridCellSizeInStuds(), + StreamRegion::Id::streamGridCellSizeInStuds()); + return v; + } + + inline const Vector3int32& gridCellHalfDimension() + { + static Vector3int32 v = Vector3int32(StreamRegion::Id::streamGridCellSizeInStuds()/2, + StreamRegion::Id::streamGridCellSizeInStuds()/2, + StreamRegion::Id::streamGridCellSizeInStuds()/2); + return v; + } + + inline Id regionContainingWorldPosition(const Vector3 &worldPos) { + // This loses precision unnecessarily, it could divide by the + // dimension size before taking floor. However this will only + // impact coordinates more than 2^31 studs away from origin. + Vector3int32 floorPos = Vector3int32::floor(worldPos); + return StreamRegion::Id(floorPos >> _PrivateConstants::kRegionDimensionInStudsAsBitShifts); + } + + inline Extents extentsFromRegionId(const Id &id) { + // This loses precision unnecessarily, it could multiply by the + // dimension size after it is converted to float. However this will + // only impact coordinates more than 2^25 studs away from origin. + Vector3int32 min = (id.value() << _PrivateConstants::kRegionDimensionInStudsAsBitShifts); + Vector3int32 max = min + (Vector3int32::one() << _PrivateConstants::kRegionDimensionInStudsAsBitShifts); + return ExtentsInt32(min, max).toExtents(); + } + + inline Id regionContainingVoxel(const Vector3int16& voxelCoordinate) { + return Id(Vector3int32(voxelCoordinate) >> + _PrivateConstants::kRegionDimensionInVoxelsAsBitShifts); + } + + inline Id regionContainingVoxel(const Vector3int32& voxelCoordinate) { + return Id(voxelCoordinate >> _PrivateConstants::kRegionDimensionInVoxelsAsBitShifts); + } + + inline Vector3int16 getMinVoxelCoordinateInsideRegion(const Id& id) { + // min terrain boundary corresponds with voxel coordinate origin + return (id.value() << + _PrivateConstants::kRegionDimensionInVoxelsAsBitShifts).toVector3int16(); + } + + inline Vector3int16 getMaxVoxelOffsetInsideRegion() { + using namespace _PrivateConstants; + return Vector3int16( + kMaxVoxelOffsetInsideRegion, + kMaxVoxelOffsetInsideRegion, + kMaxVoxelOffsetInsideRegion); + } + + inline Vector3int16 getMaxVoxelCoordinateInsideRegion(const Id& id) { + // max terrain boundary corresponds with voxel coordinate origin + return (getMinVoxelCoordinateInsideRegion(id) + getMaxVoxelOffsetInsideRegion()); + } + + inline unsigned int getTotalVoxelVolumeOfARegion() { + return 1 << (3 * _PrivateConstants::kRegionSizeInVoxelsAsBitShift); + } + + inline IdExtents regionExtentsFromContactManagerLevelAndExtents( + int level, ExtentsInt32 contactManagerExtents) { + static const int kSpatialHashMaxLevel = CONTACTMANAGER_MAXLEVELS - 1; + + // Stream region extents coincide with max spatial hash buckets + ExtentsInt32 scaledExtents = SpatialHashStatic::scaleExtents( + level, kSpatialHashMaxLevel, contactManagerExtents); + IdExtents result; + result.low = Id(scaledExtents.min()); + result.high = Id(scaledExtents.max()); + return result; + } + + inline bool coarseMovementCausesStreamRegionChange( + const ContactManagerSpatialHash::CoarseMovementCallback::UpdateInfo& info, + IdExtents* oldExtents, IdExtents* newExtents) { + + typedef ContactManagerSpatialHash::CoarseMovementCallback::UpdateInfo UpdateInfo; + + RBXASSERT_SLOW(info.updateType == UpdateInfo::UPDATE_TYPE_Change || + info.updateType == UpdateInfo::UPDATE_TYPE_Insert); + + (*newExtents) = regionExtentsFromContactManagerLevelAndExtents( + info.newLevel, info.newSpatialExtents); + + if (info.updateType == UpdateInfo::UPDATE_TYPE_Change) { + (*oldExtents) = regionExtentsFromContactManagerLevelAndExtents( + info.oldLevel, info.oldSpatialExtents); + if ((*newExtents) == (*oldExtents)) { + return false; + } + } + return true; + } + } // StreamRegion + +} // RBX diff --git a/App/util/SurfaceType.h b/App/util/SurfaceType.h new file mode 100644 index 0000000..041cefe --- /dev/null +++ b/App/util/SurfaceType.h @@ -0,0 +1,42 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ +#pragma once + +namespace RBX { + + // TODO - Joint.cpp uses this ordering - fix up as a class + + typedef enum { NO_SURFACE = 0, + GLUE, + WELD, + STUDS, + INLET, + UNIVERSAL, + ROTATE, // special ordering here + ROTATE_V, + ROTATE_P, + NO_JOIN, // new surface type for specifically preventing joining (via ManualWeldHelper) + NO_SURFACE_NO_OUTLINES, // identical to NO_SURFACE, but removes outlines + NUM_SURF_TYPES} SurfaceType; + + inline bool IsNoSurface(SurfaceType surface) + { + return surface == NO_SURFACE || surface == NO_SURFACE_NO_OUTLINES; + } + + inline bool IsRotate(SurfaceType surface) + { + return surface >= ROTATE && surface <= ROTATE_P; + } + + namespace Legacy { + // TODO: improve precedence logic + // LEGACY from when this was separate stuff + typedef enum { NO_CONSTRAINT = 0, + ROTATE_LEGACY, + ROTATE_P_LEGACY, + ROTATE_V_LEGACY, + NUM_CONSTRAINT_TYPES} SurfaceConstraint; + + } +} // namespace + diff --git a/App/util/SystemAddress.h b/App/util/SystemAddress.h new file mode 100644 index 0000000..2410486 --- /dev/null +++ b/App/util/SystemAddress.h @@ -0,0 +1,47 @@ +#pragma once + +// RakNet doesn't namespace this - ouch +namespace RBX { + +class SystemAddress +{ +public: + ///The peer address from inet_addr. + unsigned int binaryAddress; + ///The port number + unsigned short port; + + SystemAddress() + : binaryAddress(0xFFFFFFFF) + , port(0xFFFF) + {} + + SystemAddress(unsigned int binaryAddress, unsigned short port) + : binaryAddress(binaryAddress) + , port(port) + {} + + bool empty() const + { + return binaryAddress == 0xFFFFFFFF && port == 0xFFFF; + } + + void clear() + { + binaryAddress = 0xFFFFFFFF; + port = 0xFFFF; + } + + unsigned int getAddress() const { return binaryAddress; } + unsigned short getPort() const { return port; } + + bool operator == ( const SystemAddress& right ) const; + bool operator != ( const SystemAddress& right ) const; + bool operator > ( const SystemAddress& right ) const; + bool operator < ( const SystemAddress& right ) const; +}; + + + +} // namespace + diff --git a/App/util/TextureId.h b/App/util/TextureId.h new file mode 100644 index 0000000..39612d5 --- /dev/null +++ b/App/util/TextureId.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Util/ContentId.h" + +namespace RBX { + + class TextureId : public ContentId + { + public: + TextureId(const ContentId& id):ContentId(id) {} + TextureId(const char* id):ContentId(id) {} + TextureId(const std::string& id):ContentId(id) {} + TextureId() {} + + static TextureId nullTexture() { + static TextureId t; // note - the name in the contentId will get a boost call_once + return t; + } + }; +} \ No newline at end of file diff --git a/App/util/ThreadPool.h b/App/util/ThreadPool.h new file mode 100644 index 0000000..f726f76 --- /dev/null +++ b/App/util/ThreadPool.h @@ -0,0 +1,135 @@ +#pragma once + +#include "rbx/threadsafe.h" + +namespace RBX +{ + + // TODO: Implement shutdown policies: WaitForPendingTasks, InterruptTasks, etc. + class BaseThreadPool + { + public: + enum ShutdownPolicy + { + WaitForRunningTasks, + WaitForRunningTasksWithTimeout, + LockAndKill, + NoAction + }; + + struct PoolData + { + volatile bool done; + volatile bool fired; // a task has been scheduled + boost::condition_variable cond; + boost::mutex mut; + PoolData() + :done(false) + ,fired(false) + {} + virtual ~PoolData() + {} + virtual bool shouldSchedule(const BaseThreadPool *targetThreadPool) const = 0; + + virtual bool getNextTask(boost::function)>& task) = 0; + }; + private: + int count; + const size_t kMaxScheduleSize; + std::vector< boost::shared_ptr > poolLocks; + std::vector< boost::shared_ptr > pool; + ShutdownPolicy shutdownPolicy; + + static void loop(boost::shared_ptr poolData, boost::shared_ptr lock, ShutdownPolicy shutdownPolicy); + + protected: + boost::shared_ptr poolData; + bool shouldSchedule(const boost::shared_ptr &poolData) const + { + return poolData->shouldSchedule(this); + } + + void taskAdded(); + + public: + BaseThreadPool(int count, ShutdownPolicy shutdownPolicy, + PoolData* poolData, size_t maxScheduleSize); + int getThreadCount() const; + size_t getMaxScheduleSize() const + { + return kMaxScheduleSize; + } + virtual ~BaseThreadPool(); + }; + + //A ThreadPool with fifo ordering + class ThreadPool: + public BaseThreadPool + { + private: + struct ThreadPoolData + : public PoolData + { + typedef rbx::safe_queue)> > Queue; + Queue queue; + + /*override*/ bool getNextTask(boost::function)>& task) + { + return queue.pop_if_present(task); + } + + /*override*/ bool shouldSchedule(const BaseThreadPool *targetThreadPool) const + { + return 0 == targetThreadPool->getMaxScheduleSize() || queue.size() < targetThreadPool->getMaxScheduleSize(); + } + }; + protected: + + public: + ThreadPool(int count, ShutdownPolicy shutdownPolicy = NoAction, size_t maxScheduleSize = 0); + + bool schedule(boost::function)> task); + }; + + //A ThreadPool with job priorities + class PriorityThreadPool: + public BaseThreadPool + { + private: + struct PriorityTask + { + boost::function)> func; + float priority; + PriorityTask(boost::function)> func, float priority) + : func(func) + , priority(priority) + {} + PriorityTask() + {} + + bool operator<(const PriorityTask& rhs) const + { + return this->priority > rhs.priority; + } + }; + struct PriorityThreadPoolData + : public PoolData + { + typedef rbx::safe_heap Heap; + Heap heap; + + /*override*/ bool getNextTask(boost::function)>& task); + + /*override*/ bool shouldSchedule(const BaseThreadPool *targetThreadPool) const + { + return 0 == targetThreadPool->getMaxScheduleSize() || heap.size() < targetThreadPool->getMaxScheduleSize(); + } + }; + protected: + + public: + PriorityThreadPool(int count, ShutdownPolicy shutdownPolicy = NoAction, size_t maxScheduleSize = 0 ); + + bool schedule(boost::function)> task, float priority); + }; +} diff --git a/App/util/TouchType.h b/App/util/TouchType.h new file mode 100644 index 0000000..f1164ca --- /dev/null +++ b/App/util/TouchType.h @@ -0,0 +1,57 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + + +namespace RBX { + + class PVInstance; + + class TouchType { + private: + friend class PVInstance; + typedef enum {NOTHING, TOUCH_ONLY, INTERSECT} Status; + Status worldExtents; + Status objects; + + public: + TouchType() : + worldExtents(NOTHING), + objects(NOTHING) + {} + + bool intersectingWorldExtents() { + return (worldExtents == INTERSECT); + } + bool intersectingObject() { + return (objects == INTERSECT); + } + bool intersecting() { + return (intersectingWorldExtents() || intersectingObject()); + } + bool touchingWorldExtents() { + return (worldExtents == TOUCH_ONLY); + } + bool touchingObject() { + return (objects == TOUCH_ONLY); + } + bool touching() { + return (touchingWorldExtents() || touchingObject()); + } + bool touchingOnlyWorldExtents() { + return (touchingWorldExtents() && !touchingObject()); + } + bool nothingFound() { + return ((worldExtents == NOTHING) && (objects == NOTHING)); + } + bool touchingNotIntersecting() { + return (touching() && (!intersecting())); + } + bool somethingFound() { + return (!nothingFound()); + } + bool intersectsOtherOnly() { + return ((objects == INTERSECT) && (worldExtents == NOTHING)); + } + }; +} // namespace \ No newline at end of file diff --git a/App/util/UDim.h b/App/util/UDim.h new file mode 100644 index 0000000..eda6659 --- /dev/null +++ b/App/util/UDim.h @@ -0,0 +1,106 @@ +/* Copyright 2003-2009 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/G3DCore.h" + +namespace RBX { + + // A utility class for holding a "Universal Dimensions", containing both a + // relative 'scale' and an absolute 'offset' + class UDim + { + public: + float scale; + G3D::int16 offset; + + UDim(float scale, G3D::int16 offset) + :scale(scale) + ,offset(offset){} + UDim() + :scale(0.0f) + ,offset(0){} + + float transform(const float value) const; + G3D::int16 transform(G3D::int16 value) const; + // Assignment + UDim& operator=(const UDim& other) + { + scale = other.scale; + offset = other.offset; + return *this; + } + bool operator==(const UDim& other) const { + return scale==other.scale && offset==other.offset; + } + bool operator!=(const UDim& other) const { + return scale!=other.scale || offset!=other.offset; + } + UDim operator*(const G3D::int16 rhs) const; + UDim operator*(const float rhs) const; + + UDim operator+ (const UDim& v) const; + UDim operator- (const UDim& v) const; + UDim operator- () const; + }; + + class UDim2 + { + public: + UDim x; + UDim y; + + UDim2() + :x() + ,y() + {} + UDim2(UDim x, UDim y) + :x(x) + ,y(y){} + UDim2(float scaleX, int offsetX, float scaleY, int offsetY) + :x(scaleX,offsetX) + ,y(scaleY,offsetY){} + + // Assignment + UDim2& operator=(const UDim2& other) + { + x = other.x; + y = other.y; + return *this; + } + bool operator==(const UDim2& other) const { + return x == other.x && y == other.y; + } + bool operator!=(const UDim2& other) const { + return x!=other.x || y!=other.y; + } + + G3D::Vector2int16 operator*(const G3D::Vector2int16 rhs) const; + G3D::Vector2 operator*(const G3D::Vector2 rhs) const; + UDim2 operator* (float v) const; + UDim2 operator+ (const UDim2& v) const; + UDim2 operator- (const UDim2& v) const; + UDim2 operator- () const; + + + const UDim& operator[] (int i) const { + switch(i){ + case 1: + return y; + case 0: + default: + return x; + } + } + UDim& operator[] (int i){ + switch(i){ + case 1: + return y; + case 0: + default: + return x; + } + } + }; + +} // namespace RBX diff --git a/App/util/URL.h b/App/util/URL.h new file mode 100644 index 0000000..f325ae4 --- /dev/null +++ b/App/util/URL.h @@ -0,0 +1,128 @@ +#pragma once + +#include "FastLog.h" + +#include +#include +#include + +// NOTE: After removal of this fast flag, remove the HTW3C.h file and all libwww references everywhere +DYNAMIC_FASTFLAG(UseNewUrlClass); + +namespace RBX +{ + +/* A class for parsing and assembling URLs with focus mostly on HTTP-specific subset of RFC3986. + * + * Works with strings having the following form: + * https://www.watrbx.wtf/very/long/path?query&arg=value#fragment + * \___/ \____________/\_____________/ \_____________/ \______/ + * | | | | | + * scheme host path query fragment + * optional optional + * + * NOTES: + * - scheme() does not include the "://" delimiter + * - host() is expected to be a domain name. + * - path() always begins with '/' character (see normalization) + * - query() and fragment() do not include their '?' and '#' delimiters + * - URL strings with missing or empty scheme or host parts will produce invalid Url instances. + * (An invalid Url is that with isValid() == false) + * - There are no guarantees for invalid Urls. + * - Userinfo (user:password@) and :port optional parts are deliberately not handled. + * Trying to parse such URLs may result in invalid Url. + * - Url instances are currently immutable. + * - %-(un)escaping is not performed and is deliberately out of scope of this class. + * - Automatic normalization of components is performed: + * - scheme and host are lowercased + * - path sequences like //, /. and abc/.. are collapsed + * - path is forced to begin with '/' character + * - URL is invalid if: + * - scheme or host are missing/empty + * - any component contains invalid characters (see RFC3986) of malformed %-sequences + */ + +class Url +{ +public: + // Parse URL string and perform normalization + static Url fromString(const std::string& str) + { + return fromString(str.c_str()); + } + + static Url fromString(const char* str); + + // Build a new URL of provided components and perform normalization + static Url fromComponents( + const std::string& scheme, + const std::string& host, + const std::string& path = "/", + const std::string& query = "", + const std::string& fragment = "" + ); + + const std::string& scheme() const { return scheme_; } + const std::string& host() const { return host_; } + const std::string& path() const { return path_; } + const std::string& query() const { return query_; } + const std::string& fragment() const { return fragment_; } + + bool isValid() const; + + // Construct a string representation of the URL + // NOTE: may produce malformed URLs if isValid() is false for non-trivial reasons (e.g. reserved unescaped characters in wrong places) + std::string asString() const; + + bool hasValidScheme() const; + bool hasHttpScheme() const; + bool hasValidHost() const; + bool hasValidPath() const; + bool hasValidQuery() const; + bool hasValidFragment() const; + + bool pathIsEmpty() const + { + return path_.length() < 2; + } + + // Check whether the current host() is a subdomain of provided domain name. + // - "www.watrbx.wtf" is a subdomain of "watrbx.wtf" + // - "watrbx.wtf" is a subdomain of "watrbx.wtf" + // - "notwatrbx.wtf" is not a subdomain of "watrbx.wtf" + // Domain names are case insensitive + bool isSubdomainOf(const char* domain) const; + + // Check that path() is equal to path. + // Done using simple string comparison, with the exception that path argument might not begin with '/'. + bool pathEquals(const char* path) const; + + // Same as pathEquals() but performs case insensitive comparison + bool pathEqualsCaseInsensitive(const char* path) const; + + bool isSubdomainOf(const std::string& domain) const + { + return isSubdomainOf(domain.c_str()); + } + + bool pathEquals(const std::string& path) const + { + return pathEquals(path.c_str()); + } + + bool pathEqualsCaseInsensitive(const std::string& path) const + { + return pathEqualsCaseInsensitive(path.c_str()); + } + +private: + void normalize(); + + std::string scheme_; + std::string host_; + std::string path_; + std::string query_; + std::string fragment_; +}; // class Url + +} // namespace rbx \ No newline at end of file diff --git a/App/util/UintSet.h b/App/util/UintSet.h new file mode 100644 index 0000000..ceaffd3 --- /dev/null +++ b/App/util/UintSet.h @@ -0,0 +1,52 @@ +#pragma once + +#include "Util/DoubleEndedVector.h" +#include "boost/cstdint.hpp" + +namespace RBX { + +/** + * Attempts to outperform std::set by using a bitset. This will + * use less memory than a tree set when the numbers stored are close together + * (roughly, if the average spacing is less than 24 * sizeof(void*) then this + * implementation will likely use less memory than a traditional set). + */ +struct UintSet { + typedef boost::uint32_t BitGroup; + + // these two constants are public for testing + static const unsigned int kShiftForGroup; + static const BitGroup kBitInGroupMask; + +private: + struct UpdateBitInfo { + const BitGroup bitGroup; + const BitGroup bitPosition; // 0-indexed position (not a bitmask) + const BitGroup mask; + + UpdateBitInfo(unsigned int intVal); + }; + + // min group offset, group for data in bitSet[0] + unsigned int minBitGroup; + // max group offset inclusive (max valid index for bitSet) + unsigned int maxBitGroup; + // number of uints stored in this BitSet + size_t internalSize; + bool isEmpty; + // actual bitset + DoubleEndedVector bitSet; + +public: + UintSet(); + + size_t size() const; + + bool insert(const unsigned int intVal); + + bool contains(const unsigned int intVal); + + void pop_smallest(unsigned int* out); +}; + +} diff --git a/App/util/Units.h b/App/util/Units.h new file mode 100644 index 0000000..11aa103 --- /dev/null +++ b/App/util/Units.h @@ -0,0 +1,28 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#ifndef _4136E845AFB04f0d835292E319F64778 +#define _4136E845AFB04f0d835292E319F64778 + +#include "Util/G3DCore.h" + +namespace RBX { + + class Units + { + public: + static inline float mPerRbx() { return 0.05f; } + static inline float rbxPerM() { return 20.0f; } + static inline float rbxPerMM() { return rbxPerM() * 0.001f; } + static Vector3 kmsVelocityToRbx(const Vector3& kmsVelocity); + static float kmsAccelerationToRbx(float kmsAccel); + static Vector3 kmsAccelerationToRbx(const Vector3& kmsAccel); + static float kmsForceToRbx(float kmsForce); + static Vector3 kmsForceToRbx(const Vector3& kmsForce); + static Vector3 kmsTorqueToRbx(const Vector3& kmsTorque); + static float kmsDensityToRbx(float kmsDensity); + static Vector3 kmsKRotToRbx(const Vector3& kmsKRot); + static Vector3 kmsKRotDampToRbx(const Vector3& kmsKRotDamp); + }; +} // namespace + +#endif \ No newline at end of file diff --git a/App/util/UserInputBase.h b/App/util/UserInputBase.h new file mode 100644 index 0000000..a8847dd --- /dev/null +++ b/App/util/UserInputBase.h @@ -0,0 +1,76 @@ +#pragma once + +#include "Util/KeyCode.h" +#include "Util/G3DCore.h" +#include "GfxBase/TextureProxyBase.h" +#include "Util/ContentId.h" +#include "Util/TextureId.h" +#include "Util/Object.h" +#include "GfxBase/Adorn.h" + +LOGGROUP(UserInputProfile) + +namespace RBX { + + + class Adorn; + class NavKeys; + + // TODO: Rename HardwareDevice or something + class RBXBaseClass UserInputBase + { + private: + ContentId currentCursorId; + TextureProxyBaseRef currentCursor; + TextureProxyBaseRef fallbackCursor; + rbx::signals::scoped_connection unbindResourceSignal; + + void onUnbindResourceSignal(); + + protected: + virtual Vector2 getCursorPosition() = 0; + virtual TextureProxyBaseRef getGameCursor(Adorn* adorn); + TextureProxyBaseRef getCurrentCursor() { return currentCursor; } + TextureProxyBaseRef getFallbackCursor() { return fallbackCursor; } + + public: + UserInputBase(); + ~UserInputBase() {} + + rbx::signal cursorIdChangedSignal; + + // This function is purely intended for debugging and diagnostics + Vector2 getCursorPositionForDebugging() + { + return getCursorPosition(); + } + + virtual void removeJobs() {} + + ///////////////////////////////////////////////////////////////////// + // Mouse Wrapping + // + virtual void centerCursor() = 0; + + ///////////////////////////////////////////////////////////////////// + // Real-time Key Handling + // + virtual bool keyDown(KeyCode code) const = 0; + + void getNavKeys(NavKeys& navKeys,const bool shouldSuppressNavKeys) const; + + bool altKeyDown() const {return keyDown(SDLK_RALT) || keyDown(SDLK_LALT);} + bool shiftKeyDown() const {return keyDown(SDLK_RSHIFT) || keyDown(SDLK_LSHIFT);} + bool ctrlKeyDown() const {return keyDown(SDLK_RCTRL) || keyDown(SDLK_LCTRL);} + + // allows Gui Key buttons to "press" keys + virtual void setKeyState(RBX::KeyCode code, RBX::ModCode modCode, char modifiedKey, bool isDown) = 0; + + ///////////////////////////////////////////////////////////////////// + // Cursor Handling + // + ContentId getCurrentCursorId() { return currentCursorId; } + virtual bool setCursorId(RBX::Adorn *adorn, const RBX::TextureId& id); + virtual void renderGameCursor(Adorn* adorn); + }; +} // namespace diff --git a/App/util/Utilities.h b/App/util/Utilities.h new file mode 100644 index 0000000..9545a20 --- /dev/null +++ b/App/util/Utilities.h @@ -0,0 +1,102 @@ + +#ifndef _5F442CC8279E4ab292B4F86DBDCF1B27 +#define _5F442CC8279E4ab292B4F86DBDCF1B27 + +#include +#include +#include +#include "rbx/boost.hpp" +#include "rbx/Debug.h" + +namespace RBX +{ + // generic encryption functions + std::string sha1(const std::string& source); + std::string rot13(std::string source); + + bool isCamel(const char* name); + + template + class StringConverter + { + public: + static std::string convertToString(const Type& value); + static bool convertToValue(const std::string& text, Type& value); + }; + + // Note: not thread-safe + template + class copy_on_write_ptr : public boost::noncopyable + { + mutable boost::shared_ptr object; + public: + // Default constructor creates an empty pointer + copy_on_write_ptr() + {} + + // Initializes with a copy of the provided data + copy_on_write_ptr(const Class& object) + :object(new Class(object)) + {} + + // NA: 8/3/2013 Added this to make boost 1.5+ compile correctly for iOS + // "bool" operator to see if it is initialized + #if !defined( BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS ) && !defined( BOOST_NO_CXX11_NULLPTR ) + explicit operator bool () const BOOST_NOEXCEPT + { + return static_cast(object); + } + #else + operator typename boost::shared_ptr::unspecified_bool_type() const + { + return object; + } + #endif + + // For quick-accessing a value (temporary. Do not keep reference) + const Class& operator*() const + { + RBXASSERT(object.get() != 0); + return *object; + } + // For quick-accessing a value (temporary. Do not keep reference) + const Class* operator->() const + { + RBXASSERT(object.get() != 0); + return object.get(); + } + // For accessing an imutable value (as during an algorithm) + // May return an empty pointer + boost::shared_ptr read() const + { + return object; + } + + // NOTE: Don't hold on to this too long. It might change on you + boost::shared_ptr& write() + { + if (!object) + { + // create a new object to assign to + object = boost::shared_ptr(new Class()); + } + else if (object.use_count()>1) + { + // make a copy + object = boost::shared_ptr(new Class(*object)); + } + return object; + } + void reset() + { + object.reset(); + } + }; + + + +}; + + + +#endif diff --git a/App/util/VarInt.h b/App/util/VarInt.h new file mode 100644 index 0000000..33cb91d --- /dev/null +++ b/App/util/VarInt.h @@ -0,0 +1,48 @@ +#pragma once + +#include "Network/api.h" + +namespace RBX { + +template +struct VarInt { + static const unsigned char kDataMask = (1 << WindowSize) - 1; + static const unsigned char kFlagMask = (1 << WindowSize); + + template + static void encode(OutputStream& out, unsigned int count) { + RBXASSERT(WindowSize < 8); + + do { + unsigned char data = count & kDataMask; + count >>= WindowSize; + if (count) { + data |= kFlagMask; + } + out.WriteBits(&data, WindowSize + 1); + } while (count); + } + + template + static void decode(InputStream& in, unsigned int* out) { + RBXASSERT(WindowSize < 8); + + unsigned int count = 0; + (*out) = 0; + unsigned char data; + do { + data = 0; + in.ReadBits(&data, WindowSize + 1); + +// TODO: The streaming.h include needs to be moved for it to be used here. +// +// Network::readFastN( in, data ); + + (*out) |= (data & kDataMask) << (WindowSize * count); + count++; + } while (data & kFlagMask); + } + +}; + +} diff --git a/App/util/Vector3int32.h b/App/util/Vector3int32.h new file mode 100644 index 0000000..bd2ea6d --- /dev/null +++ b/App/util/Vector3int32.h @@ -0,0 +1,153 @@ + /* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "G3D/Vector3.h" +#include "G3D/Vector3int16.h" +#include "Util/Math.h" +#include "rbx/Debug.h" +#include + +namespace RBX { + + class Vector3int32 { + public: + int x; + int y; + int z; + + Vector3int32() : x(0), y(0), z(0) {} + + Vector3int32(int _x, int _y, int _z) : x(_x), y(_y), z(_z) {} + + explicit Vector3int32(const Vector3int16& v) : x(v.x), y(v.y), z(v.z) {} + + const int& operator[] (int i) const { + return ((int*)this)[i]; + } + + int& operator[] (int i) { + return ((int*)this)[i]; + } + + Vector3int32 operator- () const { + return Vector3int32(-x, -y, -z); + } + + Vector3int32 operator+ (const Vector3int32& v) const { + return Vector3int32(x + v.x, y + v.y, z + v.z); + } + + Vector3int32 operator- (const Vector3int32& v) const { + return Vector3int32(x - v.x, y - v.y, z - v.z); + } + + Vector3int32 operator* (const int v) const { + return Vector3int32(x * v, y * v, z * v); + } + + Vector3int32 operator* (const Vector3int16& v) const { + return Vector3int32(x * v.x, y * v.y, z * v.z); + } + + Vector3int32 operator* (const Vector3int32& v) const { + return Vector3int32(x * v.x, y * v.y, z * v.z); + } + + Vector3int32 operator>> (const Vector3int32& v) const { + return Vector3int32(x >> v.x, y >> v.y, z >> v.z); + } + + Vector3int32 operator>> (const Vector3int16& v) const { + return Vector3int32(x >> v.x, y >> v.y, z >> v.z); + } + + Vector3int32 operator>> (unsigned int shift) const { + RBXASSERT_SLOW(shift < 32); + return Vector3int32(x >> shift, y >> shift, z >> shift); + } + + Vector3int32 operator<< (const Vector3int32& v) const { + return Vector3int32(x << v.x, y << v.y, z << v.z); + } + + Vector3int32 operator<< (const Vector3int16& v) const { + return Vector3int32(x << v.x, y << v.y, z << v.z); + } + + Vector3int32 operator<< (unsigned int shift) const { + RBXASSERT_SLOW(shift < 32); + return Vector3int32(x << shift, y << shift, z << shift); + } + + Vector3int32 operator& (const Vector3int32& v) const { + return Vector3int32(x & v.x, y & v.y, z & v.z); + } + + Vector3int32 operator% (const Vector3int32& v) const { + return Vector3int32(x % v.x, y % v.y, z % v.z); + } + + void shiftRight(int shift) { + RBXASSERT_SLOW(shift >= 0); + RBXASSERT_SLOW(shift < 32); + x >>= shift; y >>= shift; z >>= shift; + } + + bool operator==(const Vector3int32& rkVector) const { + return ( x == rkVector.x && y == rkVector.y && z == rkVector.z ); + } + + bool operator!=(const Vector3int32& rkVector) const { + return ( x != rkVector.x || y != rkVector.y || z != rkVector.z ); + } + bool operator<(const Vector3int32& rkVector) const { + return x < rkVector.x || (x == rkVector.x && y < rkVector.y) || (x == rkVector.x && y == rkVector.y && z < rkVector.z); + } + + inline float squaredMagnitude () const { + return x*x + y*y + z*z; + } + + static Vector3int32 floor(const G3D::Vector3& v) { + return Vector3int32(Math::iFloor(v.x), Math::iFloor(v.y), Math::iFloor(v.z)); + } + + Vector3int32 min(const Vector3int32 &v) const { + return Vector3int32(std::min(v.x, x), std::min(v.y, y), std::min(v.z, z)); + } + + Vector3int32 max(const Vector3int32 &v) const { + return Vector3int32(std::max(v.x, x), std::max(v.y, y), std::max(v.z, z)); + } + + G3D::Vector3 toVector3() const { + return Vector3(static_cast(x), static_cast(y), static_cast(z)); + } + + G3D::Vector3int16 toVector3int16() const { + return Vector3int16( + static_cast(x), + static_cast(y), + static_cast(z)); + } + + int sum() const { + return x + y + z; + } + + inline static const Vector3int32& zero() { static Vector3int32 v(0, 0, 0); return v; } + inline static const Vector3int32& one() { static Vector3int32 v(1, 1, 1); return v; } + inline static const Vector3int32& maxInt() { static Vector3int32 v(INT_MAX, INT_MAX, INT_MAX); return v; } + inline static const Vector3int32& minInt() { static Vector3int32 v(INT_MIN, INT_MIN, INT_MIN); return v; } + }; + + std::ostream& operator<<(std::ostream& os, const Vector3int32& v); + ::std::size_t hash_value(const RBX::Vector3int32& v); + + inline RBX::Vector3int32 fastFloorInt32(const RBX::Vector3& v) + { + return RBX::Vector3int32(fastFloorInt(v.x), fastFloorInt(v.y), fastFloorInt(v.z)); + } + +} diff --git a/App/util/Vector6.h b/App/util/Vector6.h new file mode 100644 index 0000000..7eb0858 --- /dev/null +++ b/App/util/Vector6.h @@ -0,0 +1,29 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ +#pragma once + +template +class Vector6 +{ +private: + T data[6]; + +public: + Vector6() {;} // no initialization + Vector6(const T& setAll) + { + data[0] = data[1] = data[2] = data[3] = data[4] = data[5] = setAll; + } +// Vector6(const Vector6& _other) // use bit copy +// Vector6& operator= (const Vector6& _other); // use bit copy +// virtual ~Template(); // no destructor + + const T& operator[](int i) const + { + return data[i]; + } + + T& operator[](int i) + { + return data[i]; + } +}; diff --git a/App/util/Velocity.h b/App/util/Velocity.h new file mode 100644 index 0000000..f224d1b --- /dev/null +++ b/App/util/Velocity.h @@ -0,0 +1,80 @@ +#pragma once + +#include "Util/G3DCore.h" + +namespace RBX { + + class Velocity + { + public: + Vector3 linear; + Vector3 rotational; + + bool operator==(const Velocity& other) const { + return (linear == other.linear) && (rotational == other.rotational); + } + + bool operator!=(const Velocity& other) const { + return !(*this == other); + } + + inline Velocity() : + linear(Vector3::zero()), rotational(Vector3::zero()) {} + + Velocity(const Vector3& _linear) : + linear(_linear), rotational(Vector3::zero()) {} + + Velocity(const Vector3& _linear, const Vector3& _rotational) : + linear(_linear), rotational(_rotational) {} + + Velocity(const Velocity &other) : + linear(other.linear), rotational(other.rotational) {} + + inline Velocity operator+ (const Velocity& rhs) const { + return Velocity(linear + rhs.linear, rotational + rhs.rotational); + } + + inline Velocity operator- (const Velocity& rhs) const { + return Velocity(linear - rhs.linear, rotational - rhs.rotational); + } + + inline Velocity operator*(float f) const { + return Velocity(linear * f, rotational * f); + } + + inline Velocity operator- () const { + return Velocity(-linear, -rotational); + } + + Velocity rotateBy(const Matrix3& m) const { + return Velocity(m * linear, m * rotational); + } + + static Velocity toObjectSpace(const Velocity& vWorld, const CoordinateFrame& c) { + return vWorld.rotateBy(c.rotation.transpose()); + } + + static Velocity toWorldSpace(const Velocity& vInObject, const CoordinateFrame& c) { + return vInObject.rotateBy(c.rotation); + } + + Vector3 linearVelocityAtOffset(const Vector3& offset) const { + return linear + rotational.cross(offset); + } + + Velocity velocityAtOffset(const Vector3& offset) const { + return Velocity(linearVelocityAtOffset(offset), rotational); + } + + static const Velocity& zero() { + static Velocity v; return v; + } + + Velocity lerp(const Velocity& other, float alpha) const { + return Velocity( linear.lerp(other.linear, alpha), + rotational.lerp(other.rotational, alpha) ); + } + }; + + +} // namespace RBX diff --git a/App/util/Win/FileSystem.cpp b/App/util/Win/FileSystem.cpp index b863361..34bd59d 100644 --- a/App/util/Win/FileSystem.cpp +++ b/App/util/Win/FileSystem.cpp @@ -47,7 +47,7 @@ boost::filesystem::path getUserDirectory(bool create, FileSystemDir dir, const c } DWORD flags = create ? CSIDL_FLAG_CREATE : 0; - boost::filesystem::path robloxDir = "watrbx"; + boost::filesystem::path robloxDir = "Roblox"; if (subDirectory) robloxDir /= subDirectory; diff --git a/App/util/WinHeap.h b/App/util/WinHeap.h new file mode 100644 index 0000000..9d97764 --- /dev/null +++ b/App/util/WinHeap.h @@ -0,0 +1,11 @@ +/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +namespace RBX +{ + namespace UTIL + { + void setWindowsNoFragHeap(); + } +} diff --git a/App/util/WinInet.cpp b/App/util/WinInet.cpp index 8b7c8a9..616885f 100644 --- a/App/util/WinInet.cpp +++ b/App/util/WinInet.cpp @@ -591,4 +591,4 @@ namespace RBX } } -#endif // _WIN32 +#endif // _WIN32 \ No newline at end of file diff --git a/App/util/XboxHttp2.cpp b/App/util/XboxHttp2.cpp index d71fc89..66e335a 100644 --- a/App/util/XboxHttp2.cpp +++ b/App/util/XboxHttp2.cpp @@ -340,7 +340,7 @@ namespace RBX sysTime += (__int64)10000000 * 3600 * 24 * 365 * 100; XHR_COOKIE site1Cookie = {}; - site1Cookie.pwszUrl = L".sitetest1.pizzaboxer.fun/"; + site1Cookie.pwszUrl = L".sitetest1.robloxlabs.com/"; site1Cookie.pwszName = L"SnickerdoodleConstraint"; site1Cookie.pwszValue = L""; site1Cookie.ftExpires = (FILETIME&)sysTime; diff --git a/App/util/base64.hpp b/App/util/base64.hpp new file mode 100644 index 0000000..8e7e098 --- /dev/null +++ b/App/util/base64.hpp @@ -0,0 +1,402 @@ + +// Obtained from http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4095/ +// Erik Cassel 12/21/05 + + + +// base64.hpp +// Autor Konstantin Pilipchuk +// mailto:lostd@ukr.net +// +// + +#if !defined(__BASE64_HPP_INCLUDED__) +#define __BASE64_HPP_INCLUDED__ 1 + +#pragma once + +#include + +static +int _base64Chars[]= {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', + '0','1','2','3','4','5','6','7','8','9', + '+','/' }; + + +#define _0000_0011 0x03 +#define _1111_1100 0xFC +#define _1111_0000 0xF0 +#define _0011_0000 0x30 +#define _0011_1100 0x3C +#define _0000_1111 0x0F +#define _1100_0000 0xC0 +#define _0011_1111 0x3F + +#define _EQUAL_CHAR (-1) +#define _UNKNOWN_CHAR (-2) + +#define _IOS_FAILBIT std::ios_base::failbit +#define _IOS_EOFBIT std::ios_base::eofbit +#define _IOS_BADBIT std::ios_base::badbit +#define _IOS_GOODBIT std::ios_base::goodbit + +// TEMPLATE CLASS base64_put +template > +class base64 +{ +public: + + typedef unsigned char byte_t; + typedef _E char_type; + typedef _Tr traits_type; + + // base64 requires max line length <= 72 characters + // you can fill end of line + // it may be crlf, crlfsp, noline or other class like it + + struct lf + { + template + _OI operator()(_OI _To) const{ + *_To = _Tr::to_char_type('\n'); ++_To; + + return (_To); + } + }; + + struct crlf + { + template + _OI operator()(_OI _To) const{ + *_To = _Tr::to_char_type('\r'); ++_To; + *_To = _Tr::to_char_type('\n'); ++_To; + + return (_To); + } + }; + + + struct crlfsp + { + template + _OI operator()(_OI _To) const{ + *_To = _Tr::to_char_type('\r'); ++_To; + *_To = _Tr::to_char_type('\n'); ++_To; + *_To = _Tr::to_char_type(' '); ++_To; + + return (_To); + } + }; + + struct noline + { + template + _OI operator()(_OI _To) const{ + return (_To); + } + }; + + struct three2four + { + void zero() + { + _data[0] = 0; + _data[1] = 0; + _data[2] = 0; + } + + byte_t get_0() const + { + return _data[0]; + } + byte_t get_1() const + { + return _data[1]; + } + byte_t get_2() const + { + return _data[2]; + } + + void set_0(byte_t _ch) + { + _data[0] = _ch; + } + + void set_1(byte_t _ch) + { + _data[1] = _ch; + } + + void set_2(byte_t _ch) + { + _data[2] = _ch; + } + + // 0000 0000 1111 1111 2222 2222 + // xxxx xxxx xxxx xxxx xxxx xxxx + // 0000 0011 1111 2222 2233 3333 + + int b64_0() const {return (_data[0] & _1111_1100) >> 2;} + int b64_1() const {return ((_data[0] & _0000_0011) << 4) + ((_data[1] & _1111_0000)>>4);} + int b64_2() const {return ((_data[1] & _0000_1111) << 2) + ((_data[2] & _1100_0000)>>6);} + int b64_3() const {return (_data[2] & _0011_1111);} + + void b64_0(int _ch) {_data[0] = ((_ch & _0011_1111) << 2) | (_0000_0011 & _data[0]);} + + void b64_1(int _ch) { + _data[0] = ((_ch & _0011_0000) >> 4) | (_1111_1100 & _data[0]); + _data[1] = ((_ch & _0000_1111) << 4) | (_0000_1111 & _data[1]); } + + void b64_2(int _ch) { + _data[1] = ((_ch & _0011_1100) >> 2) | (_1111_0000 & _data[1]); + _data[2] = ((_ch & _0000_0011) << 6) | (_0011_1111 & _data[2]); } + + void b64_3(int _ch){ + _data[2] = (_ch & _0011_1111) | (_1100_0000 & _data[2]);} + + private: + byte_t _data[3]; + + }; + + template + static void encode(const char* input, size_t length, std::string& output, _Endline _Endl) + { + // Convert to base64 string + std::stringstream encodedResult; + base64 encoder; // base64 output/input in chars + int _State = 0; + std::ostreambuf_iterator _Out(encodedResult); + encoder.put(input, &input[length], _Out, _State, _Endl); + + output = encodedResult.str(); + } + + template + _II put(_II _First, _II _Last, _OI _To, _State& _St, _Endline _Endl) const + { + three2four _3to4; + int line_octets = 0; + + while(_First != _Last) + { + _3to4.zero(); + + // áåð¸ì ïî 3 ñèìâîëà + _3to4.set_0(*_First); + _First++; + + if(_First == _Last) + { + *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_0()]); ++_To; + *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_1()]); ++_To; + *_To = _Tr::to_char_type('='); ++_To; + *_To = _Tr::to_char_type('='); ++_To; + goto __end; + } + + _3to4.set_1(*_First); + _First++; + + if(_First == _Last) + { + *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_0()]); ++_To; + *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_1()]); ++_To; + *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_2()]); ++_To; + *_To = _Tr::to_char_type('='); ++_To; + goto __end; + } + + _3to4.set_2(*_First); + _First++; + + *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_0()]); ++_To; + *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_1()]); ++_To; + *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_2()]); ++_To; + *_To = _Tr::to_char_type(_base64Chars[_3to4.b64_3()]); ++_To; + + if(line_octets == 17) // base64 ïîçâîëÿåò äëèíó ñòðîêè íå áîëåå 72 ñèìâîëîâ + { + _To = _Endl(_To); + + line_octets = 0; + } + else + ++line_octets; + } + + __end: ; + + return (_First); + + } + + + template + _II get(_II _First, _II _Last, _OI _To, _State& _St) const + { + three2four _3to4; + int _Char; + + while(_First != _Last) + { + + // Take octet + _3to4.zero(); + + // -- 0 -- + // Search next valid char... + while((_Char = _getCharType(*_First)) < 0 && _Char == _UNKNOWN_CHAR) + { + if(++_First == _Last) + { + _St |= _IOS_FAILBIT|_IOS_EOFBIT; return _First; // unexpected EOF + } + } + + if(_Char == _EQUAL_CHAR){ + // Error! First character in octet can't be '=' + _St |= _IOS_FAILBIT; + return _First; + } + else + _3to4.b64_0(_Char); + + + // -- 1 -- + // Search next valid char... + while(++_First != _Last) + if((_Char = _getCharType(*_First)) != _UNKNOWN_CHAR) + break; + + if(_First == _Last) { + _St |= _IOS_FAILBIT|_IOS_EOFBIT; // unexpected EOF + return _First; + } + + if(_Char == _EQUAL_CHAR){ + // Error! Second character in octet can't be '=' + _St |= _IOS_FAILBIT; + return _First; + } + else + _3to4.b64_1(_Char); + + + // -- 2 -- + // Search next valid char... + while(++_First != _Last) + if((_Char = _getCharType(*_First)) != _UNKNOWN_CHAR) + break; + + if(_First == _Last) { + // Error! Unexpected EOF. Must be '=' or base64 character + _St |= _IOS_FAILBIT|_IOS_EOFBIT; + return _First; + } + + if(_Char == _EQUAL_CHAR){ + // OK! + _3to4.b64_2(0); + _3to4.b64_3(0); + + // chek for EOF + if(++_First == _Last) + { + // Error! Unexpected EOF. Must be '='. Ignore it. + //_St |= _IOS_BADBIT|_IOS_EOFBIT; + _St |= _IOS_EOFBIT; + } + else + if(_getCharType(*_First) != _EQUAL_CHAR) + { + // Error! Must be '='. Ignore it. + //_St |= _IOS_BADBIT; + } + else + ++_First; // Skip '=' + + // write 1 byte to output + *_To = (byte_t) _3to4.get_0(); + return _First; + } + else + _3to4.b64_2(_Char); + + + // -- 3 -- + // Search next valid char... + while(++_First != _Last) + if((_Char = _getCharType(*_First)) != _UNKNOWN_CHAR) + break; + + if(_First == _Last) { + // Unexpected EOF. It's error. But ignore it. + //_St |= _IOS_FAILBIT|_IOS_EOFBIT; + _St |= _IOS_EOFBIT; + + return _First; + } + + if(_Char == _EQUAL_CHAR) + { + // OK! + _3to4.b64_3(0); + + // write to output 2 bytes + *_To = (byte_t) _3to4.get_0(); + *_To = (byte_t) _3to4.get_1(); + + ++_First; // set position to next character + + return _First; + } + else + _3to4.b64_3(_Char); + + + // write to output 3 bytes + *_To = (byte_t) _3to4.get_0(); + *_To = (byte_t) _3to4.get_1(); + *_To = (byte_t) _3to4.get_2(); + + ++_First; + + + } // while(_First != _Last) + + return (_First); + } + +protected: + + int _getCharType(int C) const + { + if(_base64Chars[62] == C) + return 62; + + if(_base64Chars[63] == C) + return 63; + + if((_base64Chars[0] <= C) && (_base64Chars[25] >= C)) + return C - _base64Chars[0]; + + if((_base64Chars[26] <= C) && (_base64Chars[51] >= C)) + return C - _base64Chars[26] + 26; + + if((_base64Chars[52] <= C) && (_base64Chars[61] >= C)) + return C - _base64Chars[52] + 52; + + if(C == _Tr::to_int_type('=')) + return _EQUAL_CHAR; + + return _UNKNOWN_CHAR; + } + + +}; + + +#endif diff --git a/App/util/gpc.h b/App/util/gpc.h new file mode 100644 index 0000000..12566db --- /dev/null +++ b/App/util/gpc.h @@ -0,0 +1,127 @@ +/* +=========================================================================== + +Project: Generic Polygon Clipper + + A new algorithm for calculating the difference, intersection, + exclusive-or or union of arbitrary polygon sets. + +File: gpc.h +Author: Alan Murta (email: gpc@cs.man.ac.uk) +Version: 2.32 +Date: 17th December 2004 + +Copyright: (C) Advanced Interfaces Group, + University of Manchester. + + This software is free for non-commercial use. It may be copied, + modified, and redistributed provided that this copyright notice + is preserved on all copies. The intellectual property rights of + the algorithms used reside with the University of Manchester + Advanced Interfaces Group. + + You may not use this software, in whole or in part, in support + of any commercial product without the express consent of the + author. + + There is no warranty or other guarantee of fitness of this + software for any purpose. It is provided solely "as is". + +=========================================================================== +*/ + +#ifndef __gpc_h +#define __gpc_h + +//#include + +/* +=========================================================================== + Constants +=========================================================================== +*/ + +/* Increase GPC_EPSILON to encourage merging of near coincident edges */ + +#define GPC_EPSILON (DBL_EPSILON) + +#define GPC_VERSION "2.32" + + +/* +=========================================================================== + Public Data Types +=========================================================================== +*/ + +typedef enum /* Set operation type */ +{ + GPC_DIFF, /* Difference */ + GPC_INT, /* Intersection */ + GPC_XOR, /* Exclusive or */ + GPC_UNION /* Union */ +} gpc_op; + +typedef struct /* Polygon vertex structure */ +{ + double x; /* Vertex x component */ + double y; /* vertex y component */ +} gpc_vertex; + +typedef struct /* Vertex list structure */ +{ + int num_vertices; /* Number of vertices in list */ + gpc_vertex *vertex; /* Vertex array pointer */ +} gpc_vertex_list; + +typedef struct /* Polygon set structure */ +{ + int num_contours; /* Number of contours in polygon */ + int *hole; /* Hole / external contour flags */ + gpc_vertex_list *contour; /* Contour array pointer */ +} gpc_polygon; + +typedef struct /* Tristrip set structure */ +{ + int num_strips; /* Number of tristrips */ + gpc_vertex_list *strip; /* Tristrip array pointer */ +} gpc_tristrip; + + +/* +=========================================================================== + Public Function Prototypes +=========================================================================== +*/ + + +void gpc_add_contour (gpc_polygon *polygon, + gpc_vertex_list *contour, + int hole); + +void gpc_polygon_clip (gpc_op set_operation, + gpc_polygon *subject_polygon, + gpc_polygon *clip_polygon, + gpc_polygon *result_polygon); + +void gpc_tristrip_clip (gpc_op set_operation, + gpc_polygon *subject_polygon, + gpc_polygon *clip_polygon, + gpc_tristrip *result_tristrip); + +void gpc_polygon_to_tristrip (gpc_polygon *polygon, + gpc_tristrip *tristrip); + +void gpc_free_polygon (gpc_polygon *polygon); + +void gpc_free_tristrip (gpc_tristrip *tristrip); + +#endif + + +/* +=========================================================================== + End of file: gpc.h +=========================================================================== +*/ + diff --git a/App/util/md5.h b/App/util/md5.h new file mode 100644 index 0000000..00b3aca --- /dev/null +++ b/App/util/md5.h @@ -0,0 +1,54 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. + */ + +#ifdef HAVE_OPENSSL +#include +#elif !defined(_MD5_H) +#define _MD5_H + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD5_u32plus; + +typedef struct { + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + MD5_u32plus block[16]; +} MD5_CTX; + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void MD5_Init(MD5_CTX *ctx); +extern void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size); +extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/App/util/quadedge.h b/App/util/quadedge.h new file mode 100644 index 0000000..74f4eaa --- /dev/null +++ b/App/util/quadedge.h @@ -0,0 +1,325 @@ +#pragma once + +// See http://graphics.stanford.edu/courses/cs468-02-fall/readings/lischinski.ps +// And Graphics Gems IV - delaunay + +#include "rbx/Debug.h" +#include +#include "math.h" + +namespace GEMS { + + #define GEMS_EPS 1e-6 + + typedef float Real; + + class Vector2d { + public: + Real x, y; + Vector2d() { x = 0; y = 0; } + Vector2d(Real a, Real b) { x = a; y = b; } + Real norm() const; + void normalize(); + Vector2d operator+(const Vector2d&) const; + Vector2d operator-(const Vector2d&) const; + friend Vector2d operator*(Real, const Vector2d&); + friend Real dot(const Vector2d&, const Vector2d&); + }; + + class Point2d { + public: + Real x, y; + Point2d() { x = 0; y = 0; } + Point2d(Real a, Real b) { x = a; y = b; } + Point2d(const Point2d& p) { *this = p; } + Point2d operator+(const Vector2d&) const; + Vector2d operator-(const Point2d&) const; + int operator==(const Point2d&) const; + }; + + class Line { + public: + Line() {} + Line(const Point2d&, const Point2d&); + Real eval(const Point2d&) const; + int classify(const Point2d&) const; + private: + Real a, b, c; + }; + + // Vector2d: + + inline Real Vector2d::norm() const + { + return sqrt(x * x + y * y); + } + + inline void Vector2d::normalize() + { + Real len; + + if ((len = sqrt(x * x + y * y)) == 0.0) { + RBXASSERT(0); + } + else { + x /= len; + y /= len; + } + } + + inline Vector2d Vector2d::operator+(const Vector2d& v) const + { + return Vector2d(x + v.x, y + v.y); + } + + inline Vector2d Vector2d::operator-(const Vector2d& v) const + { + return Vector2d(x - v.x, y - v.y); + } + + inline Vector2d operator*(Real c, const Vector2d& v) + { + return Vector2d(c * v.x, c * v.y); + } + + inline Real dot(const Vector2d& u, const Vector2d& v) + { + return u.x * v.x + u.y * v.y; + } + + + // Point2d: + + inline Point2d Point2d::operator+(const Vector2d& v) const + { + return Point2d(x + v.x, y + v.y); + } + + inline Vector2d Point2d::operator-(const Point2d& p) const + { + return Vector2d(x - p.x, y - p.y); + } + + inline int Point2d::operator==(const Point2d& p) const + { + return ((*this - p).norm() < GEMS_EPS); + } + + + // Line: + + inline Line::Line(const Point2d& p, const Point2d& q) + // Computes the normalized line equation through the + // points p and q. + { + Vector2d t = q - p; + Real len = t.norm(); + a = t.y / len; + b = - t.x / len; + c = -(a*p.x + b*p.y); + } + + inline Real Line::eval(const Point2d& p) const + // Plugs point p into the line equation. + { + return (a * p.x + b* p.y + c); + } + + inline int Line::classify(const Point2d& p) const + // Returns -1, 0, or 1, if p is to the left of, on, + // or right of the line, respectively. + { + Real d = eval(p); + return (d < -GEMS_EPS) ? -1 : (d > GEMS_EPS ? 1 : 0); + } + + class QuadEdge; + + class Edge { + friend class QuadEdge; + friend void Splice(Edge*, Edge*); + private: + int num; + Edge *next; + Point2d *data; + public: + Edge() { data = 0; } + Edge* Rot(); + Edge* invRot(); + Edge* Sym(); + Edge* Onext(); + Edge* Oprev(); + Edge* Dnext(); + Edge* Dprev(); + Edge* Lnext(); + Edge* Lprev(); + Edge* Rnext(); + Edge* Rprev(); + Point2d* Org(); + Point2d* Dest(); + const Point2d& Org2d() const; + const Point2d& Dest2d() const; + void EndPoints(Point2d*, Point2d*); + QuadEdge* Qedge() { return (QuadEdge *)(this - num); } + + template + void visit(Func func); + + }; + + class QuadEdge { + friend Edge *MakeEdge(); + private: + Edge e[4]; + static unsigned int globalVisitId; + unsigned int myVisitId; + + public: + QuadEdge() : myVisitId(0) { + e[0].num = 0, e[1].num = 1, e[2].num = 2, e[3].num = 3; + e[0].next = &(e[0]); e[1].next = &(e[3]); + e[2].next = &(e[2]); e[3].next = &(e[1]); + } + + static void incrementVisitId() { + globalVisitId++; + } + + bool visited() { + if (myVisitId != globalVisitId) { + myVisitId = globalVisitId; + return false; + } else + return true; + } + }; + + class Subdivision { + private: + Edge *startingEdge; + Edge *Locate(const Point2d&); + public: + Subdivision(const Point2d&, const Point2d&, const Point2d&); + + void InsertSite(const Point2d&); + + template + inline void visit(Func func) + { + QuadEdge::incrementVisitId(); + startingEdge->visit(func); + } + }; + + + + /************************* Edge Algebra *************************************/ + + inline Edge* Edge::Rot() + // Return the dual of the current edge, directed from its right to its left. + { + return (num < 3) ? this + 1 : this - 3; + } + + inline Edge* Edge::invRot() + // Return the dual of the current edge, directed from its left to its right. + { + return (num > 0) ? this - 1 : this + 3; + } + + inline Edge* Edge::Sym() + // Return the edge from the destination to the origin of the current edge. + { + return (num < 2) ? this + 2 : this - 2; + } + + inline Edge* Edge::Onext() + // Return the next ccw edge around (from) the origin of the current edge. + { + return next; + } + + inline Edge* Edge::Oprev() + // Return the next cw edge around (from) the origin of the current edge. + { + return Rot()->Onext()->Rot(); + } + + inline Edge* Edge::Dnext() + // Return the next ccw edge around (into) the destination of the current edge. + { + return Sym()->Onext()->Sym(); + } + + inline Edge* Edge::Dprev() + // Return the next cw edge around (into) the destination of the current edge. + { + return invRot()->Onext()->invRot(); + } + + inline Edge* Edge::Lnext() + // Return the ccw edge around the left face following the current edge. + { + return invRot()->Onext()->Rot(); + } + + inline Edge* Edge::Lprev() + // Return the ccw edge around the left face before the current edge. + { + return Onext()->Sym(); + } + + inline Edge* Edge::Rnext() + // Return the edge around the right face ccw following the current edge. + { + return Rot()->Onext()->invRot(); + } + + inline Edge* Edge::Rprev() + // Return the edge around the right face ccw before the current edge. + { + return Sym()->Onext(); + } + + /************** Access to data pointers *************************************/ + + inline Point2d* Edge::Org() + { + return data; + } + + inline Point2d* Edge::Dest() + { + return Sym()->data; + } + + inline const Point2d& Edge::Org2d() const + { + return *data; + } + + inline const Point2d& Edge::Dest2d() const + { + return (num < 2) ? *((this + 2)->data) : *((this - 2)->data); + } + + inline void Edge::EndPoints(Point2d* por, Point2d* de) + { + data = por; + Sym()->data = de; + } + + template + void Edge::visit(Func func) + { + if (!(Qedge()->visited())) { + func(this); + Onext()->visit(func); + Oprev()->visit(func); + Dnext()->visit(func); + Dprev()->visit(func); + } + } + +} // namespace + diff --git a/App/util/rbxrandom.h b/App/util/rbxrandom.h new file mode 100644 index 0000000..6610827 --- /dev/null +++ b/App/util/rbxrandom.h @@ -0,0 +1,5 @@ +#pragma once + +namespace RBX { + unsigned int randomSeed(); +} \ No newline at end of file diff --git a/App/util/standardout.h b/App/util/standardout.h new file mode 100644 index 0000000..295da7a --- /dev/null +++ b/App/util/standardout.h @@ -0,0 +1,64 @@ +#pragma once + +#include + +#include "rbx/signal.h" +#include "boost/enable_shared_from_this.hpp" +#include + +#include "RbxFormat.h" + +namespace RBX { + + typedef enum { + MESSAGE_OUTPUT, + MESSAGE_INFO, + MESSAGE_WARNING, + MESSAGE_ERROR, + MESSAGE_SENSITIVE, + MESSAGE_TYPE_MAX + } MessageType; + + struct StandardOutMessage { + public: + MessageType type; + std::string message; + time_t time; + StandardOutMessage(MessageType type, const char* message) + :type(type),message(message) + { + ::time(&time); + } + StandardOutMessage():type(MESSAGE_OUTPUT) {} + }; + + // A very basic singleton for distributing output to diagnostics - similar to stdout & stderr combined + class StandardOut + : public boost::enable_shared_from_this + { + boost::mutex sync; + private: + // Purpose: + // Prevent creation of the class outside of the singleton class. + StandardOut(){ ; } + + public: + static shared_ptr singleton(); + + + // Value is true if warning should be allowed to print, false to ignore. + static bool allowPrintWarnings; + + rbx::signal messageOut; + + // Prints an exception if f() throws an exception (passes exception on) + // TODO: Rewrite this to handle an kind of function???? + static void print_exception(const boost::function0& f, MessageType type, bool rethrow); + + void print(MessageType type, const std::string& message); + void print(MessageType type, const char* message); + void printf(MessageType type, const char* format, ...) RBX_PRINTF_ATTR(3, 4); + void print(MessageType type, const std::exception& exp); + }; + +} diff --git a/App/util/xxhash.h b/App/util/xxhash.h new file mode 100644 index 0000000..149726c --- /dev/null +++ b/App/util/xxhash.h @@ -0,0 +1,147 @@ +/* + xxHash - Fast Hash algorithm + Header File + Copyright (C) 2012, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash source repository : http://code.google.com/p/xxhash/ +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. +*/ + +#pragma once + +#if defined (__cplusplus) +extern "C" { +#endif + +struct XXH_state32_t +{ + unsigned int seed; + unsigned int v1; + unsigned int v2; + unsigned int v3; + unsigned int v4; + unsigned long long total_len; + char memory[16]; + int memsize; +}; + +//**************************** +// Simple Hash Functions +//**************************** + +unsigned int XXH32 (const void* input, int len, unsigned int seed); + +/* +XXH32() : + Calculate the 32-bits hash of "input", of length "len" + "seed" can be used to alter the result + This function successfully passes all SMHasher tests. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s + Note that "len" is type "int", which means it is limited to 2^31-1. + If your data is larger, use the advanced functions below. +*/ + + + +//**************************** +// Advanced Hash Functions +//**************************** + +void* XXH32_init (unsigned int seed); +int XXH32_feed (void* state, const void* input, int len); +unsigned int XXH32_result (void* state); + +/* +These functions calculate the xxhash of an input provided in several small packets, +as opposed to an input provided as a single block. + +You must start with : +void* XXH32_init() +The function returns a pointer which holds the state of calculation. + +This pointer must be provided as "void* state" parameter for XXH32_feed(). +XXH32_feed() can be called as many times as necessary. +The function returns an error code, with 0 meaning OK, and all other values meaning there is an error. +Note that "len" is type "int", which means it is limited to 2^31-1. +If your data is larger, it is recommended +to chunk your data into blocks of size 2^30 (1GB) to avoid any "int" overflow issue. + +Finally, you can end the calculation anytime, by using XXH32_result(). +This function returns the final 32-bits hash. +You must provide the same "void* state" parameter created by XXH32_init(). + +Memory will be freed by XXH32_result(). +*/ + + +unsigned int XXH32_getIntermediateResult (void* state); +/* +This function does the same as XXH32_result(), generating a 32-bit hash, +but preserve memory context. +This way, it becomes possible to generate intermediate hashes, and then continue feeding data with XXH32_feed(). +To free memory context, use XXH32_result(). +*/ + + +unsigned int XXH32_getRbxNonce(unsigned int base, unsigned int query); +/* +This inverts the xxhash steps that gave us "base" up to the point where size +was added. query size will be 4 bytes larger. Then xxhash steps for "query" +are inverted up to where data is added. This results in an expression where +the 4 bytes of added data can be exactly determined. +*/ + + +#if defined (__cplusplus) +} +#endif diff --git a/App/v8datamodel/DataModel.cpp b/App/v8datamodel/DataModel.cpp index 4a2a02c..0ccd95f 100644 --- a/App/v8datamodel/DataModel.cpp +++ b/App/v8datamodel/DataModel.cpp @@ -221,7 +221,6 @@ static Reflection::BoundFuncDesc toggleFunction(&DataModel::t static Reflection::PropDescriptor prop_isPersonalServer("IsPersonalServer", category_State, &DataModel::getIsPersonalServer, &DataModel::setIsPersonalServer, Reflection::PropertyDescriptor::SCRIPTING, Security::RobloxScript); static Reflection::PropDescriptor prop_canSaveLocal("LocalSaveEnabled", category_State, &DataModel::canSaveLocal, NULL, Reflection::PropertyDescriptor::UI, Security::RobloxScript); -static Reflection::PropDescriptor prop_isXboxApp("isXboxApp", category_State, &DataModel::getIsXboxApp, &DataModel::setIsXboxApp, Reflection::PropertyDescriptor::UI, Security::RobloxScript); static Reflection::BoundYieldFuncDesc saveToRobloxFunction(&DataModel::saveToRoblox, "SaveToRoblox", Security::RobloxScript); @@ -526,7 +525,6 @@ private: } }; -bool RBX::DataModel::isXboxApp = false; static std::string tempTag() { @@ -543,9 +541,6 @@ bool DataModel::canSave(const RBX::Instance* instance) return true; } -void DataModel::setIsXboxApp(bool isXboxApp) { - this->isXboxApp = isXboxApp; -} bool DataModel::serverSavePlace(const SaveFilter saveFilter, boost::function resumeFunction, boost::function errorFunction) { diff --git a/App/v8datamodel/DataStore.cpp b/App/v8datamodel/DataStore.cpp index 847e7fa..4c429a3 100644 --- a/App/v8datamodel/DataStore.cpp +++ b/App/v8datamodel/DataStore.cpp @@ -463,7 +463,7 @@ namespace RBX { if (itData == result->end()) { std::string msg = response ? *response : "null"; - FASTLOGS(FLog::DataStore, "Failed to retrieve key %s. Response: %s", key.c_str(), msg.c_str()); + //FASTLOGS(FLog::DataStore, "Failed to retrieve key %s. Response: %s", key.c_str(), msg.c_str()); errorFunction("Failed to retrieve key"); return; } diff --git a/App/v8kernel/Body.h b/App/v8kernel/Body.h new file mode 100644 index 0000000..8e4e75b --- /dev/null +++ b/App/v8kernel/Body.h @@ -0,0 +1,415 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Kernel/KernelIndex.h" +#include "V8Kernel/Cofm.h" +#include "V8Kernel/SimBody.h" +#include "V8Kernel/Link.h" +#include "Util/IndexArray.h" +#include "Util/IndexedTree.h" +#include "Util/Memory.h" +#include "rbx/threadsafe.h" + +class btCollisionObject; + +namespace RBX { + + /* + Body class. Handles rigid joints and kinematic/dynamic joints. Automatically calculates + mass properties. Automatically updates state. + + StateRoot: Body or Joint that contains the latest state id. + RigidRoot: Body that I am clumped with + COFM: Calculated for every body in a rigid group - chain is broken for linked bodies + SIMBody: Only Free Bodies have this + + Whenever adjusting a joint, must increment the stateRoot of the assembly + + World Body // Special Body - no COFM + | | + | -- Body // anchored body - rigid join to the world body - when moving, adjust state index + | + -- Body // anchored body - rigid join to the world body + | + -- Body + | + -- Body + 0 // this is a link + -- Body // this is the StateRoot for the chain below it + | + Body + + Body (free) // this is the StateRoot for the free body and chain + | // this body has group COFM for the top three bodies + SimBody + -- Body + | + -- Body + 0 + -- Body // this body has a group COFM for the bodies below it + | + Body + + COFM: body, kinematic, assembly (assumes a floating object) + + */ + + class BodyPvSetter; + + class Body : public IndexedTree + , public Allocator + { + public: + friend class KernelData; + friend class Kernel; + friend class SimBody; + + private: + rbx::spin_mutex mutex; // for safe calls that require update + + // Unique identifier set by the World + boost::uint64_t uid; + int guidIndex; + + int leafBodyIndex; + + int& getLeafBodyIndex() {return leafBodyIndex;} + + int connectorUseCount; // how many connectors connect this body + static Body* worldBody; + static void initStaticData(); // inits the world body + + Body* root; // top body - either anchored or 6 dof + + Cofm* cofm; + Cofm* getCofm() {return cofm;} // use these to insure const correctness; + + SimBody* simBody; // Only present for parent && in kernel + SimBody* getSimBody() {return simBody;} + const SimBody* getConstSimBody() const {return simBody;} + + void refreshCofm(); + + Link* link; // if link != NULL, use for "getMeInParent()" + + // defining variables + bool canThrottle; // this body can throttle - i.e., slow down + CoordinateFrame meInParent; // used if no link, i.e. - rigid connection + Matrix3 moment; + float mass; + Vector3 cofmOffset; + + // resulting variables + unsigned int stateIndex; + PV pv; + + void resetRoot(Body* newRoot); + + bool validateParentCofmDirty(); + + const CoordinateFrame& getMeInParent() { + RBXASSERT(getParent()); + return getLink() ? getLink()->getChildInParent() : meInParent; + } + const CoordinateFrame& getConstMeInParent() const { + RBXASSERT(getConstParent()); + RBXASSERT(!getConstLink()); // fails if linked (i.e. only works in same clump); + return meInParent; + } + + void updatePV(); // Does not inline anyhow. + + bool pvIsUpToDate() const { + if (!getConstParent()) { + return true; + } + else { + if (stateIndex != getRoot()->getStateIndexNoUpdate()) { + return false; + } + else { + return getConstParent()->pvIsUpToDate(); + } + } + } + + void onChildAdded(Body* newChild); + void onChildRemoved(Body* newChild); + + Body* calcRoot() {return getParent() ? getParent()->calcRoot() : this;} + const Body* calcRootConst() const {return getConstParent() ? getConstParent()->calcRootConst() : this;} + + /////////////////////////////////////////////// + // Indexed Tree + /*override*/ void onParentChanging(); + /*override*/ void onParentChanged(IndexedTree* oldParent); + /*override*/ void onChildAdding(IndexedTree* child); + /*override*/ void onChildAdded(IndexedTree* child); + /*override*/ void onChildRemoved(IndexedTree* child); + + public: + Body(); + + ~Body(); + + static unsigned int getNextStateIndex(); + + // Static world body + static Body* getWorldBody(); + + SimBody* getRootSimBody() {return getRoot()->getSimBody();} + const SimBody* getConstRootSimBody() const {return getRoot()->getConstSimBody();} + + void setUID( boost::uint64_t _uid ); + boost::uint64_t getUID() const { return uid; } + + // Used only by the solver inspector to id the objects between different clients + void setGuidIndex( int _guidIndex ) { guidIndex = _guidIndex; } + int getGuidIndex() const { return guidIndex; } + + ////////////////////////////////////////////////////// + // From Cofm, SimBody + bool cofmIsClean() {return getCofm() ? !getCofm()->getIsDirty() : true;} + + ////////////////////////////////////////////////////// + // From Child + void makeCofmDirty(); + + void advanceStateIndex(); + + void makeStateDirty() { + getRoot()->advanceStateIndex(); + } + + unsigned int getStateIndex() { + updatePV(); + return stateIndex; + } + + unsigned int getStateIndexNoUpdate() const { + return stateIndex; + } + + ////////////////////////////////////////////////////// + // Base vs. Branch + + Body* getChild(int i) {return getTypedChild(i);} + const Body* getConstChild(int i) const {return getConstTypedChild(i);} + + Body* getParent() {return getTypedParent();} + const Body* getConstParent() const {return getConstTypedParent();} + + Link* getLink() {return link;} + const Link* getConstLink() const {return link;} + + const Body* getRoot() const { + RBXASSERT_SLOW(root == calcRootConst()); + return root; + } + + Body* getRoot() { + RBXASSERT_SLOW(root == calcRootConst()); + return root; + } + + const Vector3& getCofmOffset() { + return cofmOffset; + } + + const Vector3& getBranchCofmOffset(); + + // Only works on bodies in same clump - otherwise not const for the link and will assert + CoordinateFrame getMeInAncestor(const Body* ancestor) const { + if (ancestor == this) { + return CoordinateFrame(); + } + else if (ancestor == getConstParent()) { + return getConstMeInParent(); + } + else { + return getConstParent()->getMeInAncestor(ancestor) * getConstMeInParent(); + } + } + + inline float getMass() const {return mass;} + inline Matrix3 getIBody() const {return moment;} + inline Vector3 getIBodyV3() const {return Math::toDiagonal(getIBody());} + Matrix3 getIBodyAtPoint(const Vector3& point); + inline Matrix3 getMoment() const {return getIBody();} + inline Vector3 getPrincipalMoment() const {return getIBodyV3();} + Matrix3 getIWorld() {return Math::momentToWorldSpace(getIBody(), getCoordinateFrame().rotation);} + Matrix3 getIWorldAtPoint(const Vector3& point); + + // Branch refers to everything below me.... + float getBranchMass() {return getCofm() ? getCofm()->getMass() : mass;} + Matrix3 getBranchIBody() {return getCofm() ? getCofm()->getMoment() : moment;} + Vector3 getBranchIBodyV3() {return Math::toDiagonal(getBranchIBody());} + Matrix3 getBranchIWorld() {return Math::momentToWorldSpace(getBranchIBody(), getCoordinateFrame().rotation);} + Matrix3 getBranchIWorldAtPoint(const Vector3& point); + Vector3 getBranchCofmPos(); + CoordinateFrame getBranchCofmCoordinateFrame(); + + // + + const PV& getPvFast() const { + RBXASSERT_FISHING(pvIsUpToDate()); + return pv; + } + + // Current Job should hold the Data Model write lock or somehow have locked the Body::mutex. + const PV& getPvUnsafe() { + updatePV(); + return pv; + } + + const PV& getPV_Spin_Lock() { + rbx::spin_mutex::scoped_lock lock(mutex); + updatePV(); + return pv; + } + + const PV& getPvSafe() const { + Body* thisNotConst = const_cast(this); + return thisNotConst->getPV_Spin_Lock(); + } + + const Vector3& getPosFast() const { + RBXASSERT_FISHING(pvIsUpToDate()); + return pv.position.translation; + } + + const Vector3& getPos() { + updatePV(); + return pv.position.translation; + } + + const CoordinateFrame& getCoordinateFrameFast() const { + RBXASSERT_FISHING(pvIsUpToDate()); + return pv.position; + } + + const CoordinateFrame& getCoordinateFrame() { + updatePV(); + return pv.position; + } + + const Velocity& getVelocity() { + updatePV(); + return pv.velocity; + } + + bool getCanThrottle() const { + return canThrottle; + } + + void accumulateImpulseAtBranchCofm(const Vector3& impulse) { + if (SimBody* s = getRootSimBody()) { + s->accumulateImpulseAtBranchCofm(impulse); + } + } + + void accumulateLinearImpulse(const Vector3& impulse, const Vector3& worldPos) { + if (SimBody* s = getRootSimBody()) { + s->accumulateImpulse(impulse, worldPos); + } + } + + void accumulateRotationalImpulse(const Vector3& impulse) { + if (SimBody* s = getRootSimBody()) { + s->accumulateRotationalImpulse(impulse); + } + } + + void accumulateForceAtBranchCofm(const Vector3& force) { + RBXASSERT(getRoot() == this); // should only be called on Root objects + if (SimBody* s = getRootSimBody()) { + s->accumulateForceCofm(force); + } + } + + void accumulateForce(const Vector3& force, const Vector3& worldPos) { + if (SimBody* s = getRootSimBody()) { + s->accumulateForce(force, worldPos); + } + } + + void accumulateTorque(const Vector3& torque) { + if (SimBody* s = getRootSimBody()) { + s->accumulateTorque(torque); + } + } + + void resetForceAccumulators() { + if (SimBody* s = getRootSimBody()) { + s->resetForceAccumulators(); + } + } + + void resetImpulseAccumulators() { + if (SimBody* s = getRootSimBody()) { + s->resetImpulseAccumulators(); + } + } + + const Vector3& getBranchForce() const { + RBXASSERT(getRoot() == this); // should only be called on Root objects + const SimBody* s = getConstRootSimBody(); + return s ? s->getForce() : Vector3::zero(); + } + + const Vector3& getBranchTorque() const { + RBXASSERT(getRoot() == this); // should only be called on Root objects + const SimBody* s = getConstRootSimBody(); + return s ? s->getTorque() : Vector3::zero(); + } + + const Velocity& getBranchVelocity() { // velocity at the COFM of the assembly + RBXASSERT(getRoot() == this); // should only be called on Root objects + const SimBody* s = getRootSimBody(); + return s ? s->getPV().velocity : Velocity::zero(); + } + + ///////////////////////////////////////////////////// + // setting properties + // + void setParent(Body* parent) {setIndexedTreeParent(parent);} + + void setMeInParent(const CoordinateFrame& _meInParent); + + void setMeInParent(Link* _link); + + void setMass(float _mass); + + void setMoment(const Matrix3& _momentInBody); + + void setCofmOffset(const Vector3& _centerOfMassInBody); + + + // Only Primitive can set these - make sure we keep byproducts updated - fuzzy extents, etc. + // ToDo: Hack - is there a better way to limit access to only primitives, while not including "friend class Primitive"??? + + void setPv(const PV& _pv, const BodyPvSetter& bpv); + + void setCoordinateFrame(const CoordinateFrame& worldCoord, const BodyPvSetter& bpv); + + void setVelocity(const Velocity& worldVelocity, const BodyPvSetter& bpv); + + void setCanThrottle(bool value, const BodyPvSetter& bpv); + + void updateBulletCollisionObject(btCollisionObject* object); + + public: + + ///////////////////////////////////////////////////////// + // Debugging / reporting functions / complex stuff + // + + inline bool isLeafBody() const {return leafBodyIndex >= 0;} + + float kineticEnergy(); + float potentialEnergy(); + + }; + +} // namespace + diff --git a/App/v8kernel/BodyPvSetter.h b/App/v8kernel/BodyPvSetter.h new file mode 100644 index 0000000..b84a8b7 --- /dev/null +++ b/App/v8kernel/BodyPvSetter.h @@ -0,0 +1,13 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +namespace RBX { + + // Stub class - primitive descends from this to protect body::setCoordinateFrame() from others + class BodyPvSetter + { + }; + +} // namespace + diff --git a/App/v8kernel/BulletShapeConnectors.h b/App/v8kernel/BulletShapeConnectors.h new file mode 100644 index 0000000..bcff8b5 --- /dev/null +++ b/App/v8kernel/BulletShapeConnectors.h @@ -0,0 +1,111 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Kernel/ContactParams.h" +#include "V8Kernel/PolyConnectors.h" +#include "Util/G3DCore.h" +#include "rbx/Debug.h" + +#include "BulletCollision/NarrowphaseCollision/btPersistentManifold.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "btBulletCollisionCommon.h" + + +namespace RBX { + + class BulletShapeConnector : public PolyConnector, + public Allocator + { + protected: + btCollisionObject* bulletCollisionObject0; + btCollisionObject* bulletCollisionObject1; + btCollisionAlgorithm* bulletAlgo; + int bulletManifoldIndex; + int bulletPointCacheIndex; + + void updateConnectorPointFromManifold(bool refreshContacts = true); + void realignConnectorsToBulletContacts(); + bool foundValidContactPointFromBulletManifold(btPersistentManifold* man, Vector3& p0World, Vector3& p1World); + + private: + /*override*/ GeoPairType getConnectorType() const {return BULLET_SHAPE_CONNECTOR;} + bool validObjectCFrames(); + virtual void updateBulletCollisionObjects(); + + public: + BulletShapeConnector( + Body* b0, + Body* b1, + const ContactParams& contactParams, + btCollisionObject* bulletColObj0, + btCollisionObject* bulletColObj1, + btCollisionAlgorithm* algo, + int manifoldIndex, + int cacheIndex + ) + : PolyConnector(b0, b1, contactParams, 0, 0) + , bulletCollisionObject0(bulletColObj0) + , bulletCollisionObject1(bulletColObj1) + , bulletAlgo(algo) + , bulletManifoldIndex(manifoldIndex) + , bulletPointCacheIndex(cacheIndex) + { + } + + ~BulletShapeConnector(); + + /*override*/ void updateContactPoint(); + void findValidContactAfterNarrowphase(); + bool recalculateValidPoints(btManifoldArray& btManArray, Vector3& pt0InWorld, Vector3& pt1InWorld); + void setBulletManifoldPointIndex(int index) { bulletPointCacheIndex = index; } + int getBulletManifoldIndex(void) { return bulletManifoldIndex;} + int getBulletPointCacheIndex(void) { return bulletPointCacheIndex;} + + void refreshIndividualPoint(bool swapped, Vector3 pt0InWorld, Vector3 pt1InWorld, btManifoldArray& manArray); + void updatePointWithTransform(bool swapped, btManifoldPoint& manifoldPoint); + bool isPointInvalid( btManifoldPoint& manifoldPoint, double validThreshold); + + static bool match(BulletShapeConnector* oldCon, BulletShapeConnector* newCon) + { + return ((oldCon->bulletManifoldIndex == newCon->bulletManifoldIndex) + && (oldCon->bulletPointCacheIndex == newCon->bulletPointCacheIndex) + && (oldCon->getConnectorType() == newCon->getConnectorType())); + } + }; + + class BulletShapeCellConnector : public BulletShapeConnector + { + private: + /*override*/ GeoPairType getConnectorType() const {return BULLET_SHAPE_CELL_CONNECTOR;} + /*override*/ void updateBulletCollisionObjects(); + + + public: + BulletShapeCellConnector( + Body* b0, + Body* b1, + const ContactParams& contactParams, + btCollisionObject* bulletColObj0, + btCollisionObject* bulletColObj1, + btCollisionAlgorithm* algo, + int manifoldIndex, + int cacheIndex + ) + : BulletShapeConnector(b0, b1, contactParams, bulletColObj0, bulletColObj1, algo, manifoldIndex, cacheIndex) + { + } + ~BulletShapeCellConnector() {} + + /*override*/ void updateContactPoint(); + + static bool match(BulletShapeCellConnector* oldCon, BulletShapeCellConnector* newCon) + { + return ((oldCon->bulletManifoldIndex == newCon->bulletManifoldIndex) + && (oldCon->bulletPointCacheIndex == newCon->bulletPointCacheIndex) + && (oldCon->getConnectorType() == newCon->getConnectorType())); + } + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/v8kernel/BuoyancyConnector.h b/App/v8kernel/BuoyancyConnector.h new file mode 100644 index 0000000..69a01f3 --- /dev/null +++ b/App/v8kernel/BuoyancyConnector.h @@ -0,0 +1,36 @@ +#pragma once + +#include "v8kernel/ContactConnector.h" + +namespace RBX { + + class BuoyancyConnector : public RBX::ContactConnector + { + private: + Vector3 position; // force application point in object space + Vector3 force; + Vector3 torque; + + float floatDistance; + float sinkDistance; + float submergeRatio; + + protected: + /*override*/ void computeForce( bool throttling ); + /*override*/ virtual KernelType getConnectorKernelType() const { return Connector::BUOYANCY; } + + public: + void updateContactPoint(); // Only for debug rendering now + + const Vector3& getPosition() { return position; } + const Vector3 getWorldPosition(); + void setForce( const Vector3& f ) { force = f; } + void setTorque( const Vector3& t ) { torque = t; } + void getWaterBand( float& up, float& down ) { up = floatDistance; down = sinkDistance; } + void setWaterBand( const float& up, const float& down ) { floatDistance = up; sinkDistance = down; } + float getSubMergeRatio() { return submergeRatio; } + void setSubMergeRatio( const float& ratio ) { submergeRatio = ratio; } + + BuoyancyConnector(Body* b0, Body* b1, const Vector3& pos); + }; +} \ No newline at end of file diff --git a/App/v8kernel/Cofm.h b/App/v8kernel/Cofm.h new file mode 100644 index 0000000..36d1d06 --- /dev/null +++ b/App/v8kernel/Cofm.h @@ -0,0 +1,48 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/G3DCore.h" +#include "Util/Memory.h" + +namespace RBX { + + class Body; + + class Cofm : public Allocator + { + private: + Body* body; + bool dirty; + Vector3 cofmInBody; + float mass; + Matrix3 moment; + + void updateIfDirty(); // true if was dirty + + public: + Cofm(Body* body); + + bool getIsDirty() const {return dirty;} + + void makeDirty() { + dirty = true; + } + + const Vector3& getCofmInBody() { + updateIfDirty(); + return cofmInBody; + } + + float getMass() { + updateIfDirty(); + return mass; + } + + const Matrix3& getMoment() { + updateIfDirty(); + return moment; + } + }; + +} // namespace diff --git a/App/v8kernel/Connector.h b/App/v8kernel/Connector.h new file mode 100644 index 0000000..5b3802e --- /dev/null +++ b/App/v8kernel/Connector.h @@ -0,0 +1,218 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/NormalID.h" +#include "rbx/Debug.h" +#include "Util/Memory.h" +#include "Util/Math.h" + + +namespace RBX { + + class Point; + class Body; + class Kernel; + + class RBXBaseClass Connector + { + friend class KernelData; + friend class Kernel; + private: + int humanoidIndex; + int realTimeIndex; + int secondPassIndex; + int jointIndex; + int buoyancyIndex; + int contactIndex; + + protected: + // Used by kernel only. Only add types that KernelData.h can handle. + typedef enum { + CONTACT, + JOINT, + HUMANOID, + KERNEL_JOINT, + BUOYANCY + } KernelType; + + /*implement*/ virtual KernelType getConnectorKernelType() const = 0; + + public: + + int& getHumanoidIndex() {return humanoidIndex;} + int& getRealTimeIndex() {return realTimeIndex;} + int& getSecondPassIndex() {return secondPassIndex;} + int& getJointIndex() {return jointIndex;} + int& getBuoyancyIndex() {return buoyancyIndex;} + int& getContactIndex() {return contactIndex;} + bool isHumanoid() {return humanoidIndex >= 0;} + bool isRealTime() {return realTimeIndex >= 0;} + bool isSecondPass() {return secondPassIndex >= 0;} + bool isJoint() {return jointIndex >= 0;} + bool isBuoyancy() {return buoyancyIndex >= 0;} + bool isContact() {return contactIndex >= 0;} + bool isInKernel() {return isHumanoid() || isRealTime() || isSecondPass() || isJoint() || isBuoyancy() || isContact();} + + Connector() : humanoidIndex(-1), realTimeIndex(-1), secondPassIndex(-1), + jointIndex(-1), buoyancyIndex(-1), contactIndex(-1) {} + virtual ~Connector() {} + + virtual bool computeCanThrottle(); + + /////////// Called by kernel ////////////////////////////// + virtual void computeForce(bool throttling) = 0; + virtual bool computeImpulse(float& residualVelocity) {return false;} + virtual bool getBroken() {return false;} + + typedef enum { body0, body1 } BodyIndex; + virtual Body* getBody(BodyIndex id) = 0; + + // DEBUGGING + virtual float potentialEnergy() {return 0.0;} + }; + + class JointConnector + : public Connector + { + protected: + /*override*/ virtual KernelType getConnectorKernelType() const {return JOINT;} + }; + + ////////////////////////////////////////////////////////////////////////// + + // Force = kForce * d_length + // Torque = kTorque * d_angle + // d_length = d_angle * L; + // d_angle= d_length / L + // This should produce an equivalent "force" at a length L from the center. + // so, force at a distance of L is Force = L * torque; + // F = kTorque * d_angle / L = kForce * d_L + // kTorque = kForce * d_L * L * L / d_L = kForce * L * L; + + class RotateConnector + : public JointConnector + { + private: + float baseRotation; // rotation when assembled + + protected: + Body* b0; + Body* b1; + CoordinateFrame j0; + CoordinateFrame j1; + + float k; // spring constant + // Integrator properties + float currentAngle; + float desiredAngle; + float increment; + bool zeroVelocity; + + float computeNormalRotation(Vector3& normal); + + float computeNormalRotationFromBase(Vector3& normal); + float computeNormalRotationFromBaseFast(Vector3& normal); + + virtual void stepGoals(); + + /*override*/ Body* getBody(BodyIndex id); + + /////////// Called by kernel ////////////////////////////// + /*override*/ virtual void computeForce(bool throttling); + + public: + RotateConnector( + Body* _b0, + Body* _b1, + const CoordinateFrame& _j0, + const CoordinateFrame& _j1, + float _baseAngle, + float kValue, + float armLength); + + void reset(); // after networking receive - update to synch internal desiredRotation + + void setRotationalGoal(float rotationalGoal); + + void setVelocityGoal(float velocity); + + static float computeJointAngle( + const CoordinateFrame& b0, + const CoordinateFrame& b1, + const CoordinateFrame& j0, + const CoordinateFrame& j1, + Vector3& normal); + }; + + ////////////////////////////////////////////////////////////////////////// + + class PointToPointBreakConnector + : public JointConnector + { + protected: + Point* point0; + Point* point1; + float k; // spring constant + float breakForce; + + // state variable + bool broken; + + void forceToPoints(const G3D::Vector3& force); + + /*override*/ Body* getBody(BodyIndex id); + + public: + // initialize + PointToPointBreakConnector(Point* point0, Point* point1, float k, float breakForce) : + point0(point0), + point1(point1), + k(k), + breakForce(breakForce), + broken(false) + {} + + /////////// Called by kernel ////////////////////////////// + /*override*/ virtual void computeForce(bool throttling); + + /*override*/ virtual bool getBroken() { return broken; } + + /* override */ virtual float potentialEnergy(); + + ///////// for breakage - one "Joint" may need to break all connectors + inline void setBroken() { broken = true; } + + float getStiffness() const { return k; } + void setStiffness( float value ) { k = value; } + }; + + + + ////////////////////////////////////////////////////////////////////////// + + // TODO: update normal very infrequently.... + + class NormalBreakConnector + : public PointToPointBreakConnector + , public Allocator + { + private: + NormalId normalIdBody0; + + public: + NormalBreakConnector( + Point* point0, + Point* point1, + float k, + float breakForce, + NormalId normalIdBody0) + : PointToPointBreakConnector(point0, point1, k, breakForce) + , normalIdBody0(normalIdBody0) + {} + + /////////// Called by kernel ////////////////////////////// + /*override*/ virtual void computeForce(bool throttling); + }; + +} // namespace \ No newline at end of file diff --git a/App/v8kernel/Constants.h b/App/v8kernel/Constants.h new file mode 100644 index 0000000..fe81e96 --- /dev/null +++ b/App/v8kernel/Constants.h @@ -0,0 +1,62 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/G3DCore.h" + +namespace RBX { + + class Constants { + private: + static const int JOINT_FORCE_DATA = 7; + static const float MAX_LEGO_JOINT_FORCES_THEORY[JOINT_FORCE_DATA]; + static const float MAX_LEGO_JOINT_FORCES_MEASURED[JOINT_FORCE_DATA]; + + static float LEGO_GRID_MASS(); // kg + static float LEGO_JOINT_K(); // kg/s^2 + static float LEGO_DEFAULT_ELASTIC_K(); + + static float unitJointK(); + + static float getJointKMultiplier(const Vector3& clippedSortedSize, bool ball); + + Constants(); + + public: + /////////////////////////////////////////////////////////////////// + // + // Timestep related stuff + // + static int uiStepsPerSec() {return longUiStepsPerSec() * 2;} + static int worldStepsPerUiStep(); + static int longUiStepsPerSec() {return 30;} + static int worldStepsPerLongUiStep(); + static int kernelStepsPerWorldStep(); + static int freeFallStepsPerWorldStep(); + static int worldStepsPerSec(); + static int kernelStepsPerSec(); + static int kernelStepsPerUiStep(); + static int freeFallStepsPerSec(); + static int impulseSolverMaxIterations(); + static float impulseSolverAccuracy(); + static int impulseSolverAccuracyScalar(); + static float impulseSolverSymStateTorqueBound(); + static float impulseSolverSymStateForceBound(); + static float uiDt(); + static float longUiStepDt(); + static float worldDt(); + static float kernelDt(); + static float freeFallDt(); + static const Vector3& denormalSmall(); + + ////////////////////////////////////////////////////////////////// + // + // Dimenensions and K related stuff + // + static inline float getKmsGravity() {return -9.81f;} + static float getKmsMaxJointForce(float grid1, float grid2); + static float getElasticMultiplier(float elasticity); + static float getJointK(const Vector3& size, bool ball); // kg/s^2 +}; + +} // namespace \ No newline at end of file diff --git a/App/v8kernel/ContactConnector.h b/App/v8kernel/ContactConnector.h new file mode 100644 index 0000000..d339867 --- /dev/null +++ b/App/v8kernel/ContactConnector.h @@ -0,0 +1,220 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Kernel/Connector.h" +#include "V8Kernel/ContactParams.h" +#include "V8Kernel/Pair.h" +#include "V8Kernel/Body.h" +#include "v8kernel/SimBody.h" +#include "v8kernel/Constants.h" +#include "Util/NormalID.h" +#include "rbx/Debug.h" +#include "Util/Memory.h" + + +namespace RBX { + + ////////////////////////////////////////////////////////////////////////// + class ContactConnector : public Connector + { + private: + static int inContactHit; + static int outOfContactHit; + + int age; + // Cache for computeImpulse + Matrix3 deltaVelPerUnitImpulse; + Matrix3 impulsePerUnitDeltaVel; + float inverseMass; + float penetrationVelocity; + float reboundVelocity; + bool impulseComputed; + + protected: + GeoPair geoPair; + ContactParams contactParams; + PairParams oldContactPoint; + PairParams contactPoint; + + // state variables + float firstApproach; + float threshold; + + // delay variables + float forceMagLast; // contact only variable + Vector3 frictionOffset; + + /*override*/ virtual KernelType getConnectorKernelType() const { return Connector::CONTACT; } + + public: + virtual void updateContactPoint(); + + static float overlapGoal() {return 0.01f;} // standard goal seek for overlapping objects + + /*override*/ Body* getBody(BodyIndex id) {return (id == body0) ? geoPair.body0 : geoPair.body1;} + void setBody(int id, Body* b) { + if (id == 0) { + geoPair.body0 = b; + } + else { + geoPair.body1 = b; + } + } + + ContactConnector(Body* b0, Body* b1, const ContactParams& contactParams) + : contactParams(contactParams), inverseMass(0.0f), impulseComputed(false) + { + geoPair.body0 = b0; + geoPair.body1 = b1; + reset(); + } + + void reset() { // cleans up state variables for buffered version + firstApproach = 0.0; + threshold = 0.0; + forceMagLast = 0.0; + age = 0; + penetrationVelocity = 0.0; + reboundVelocity = 0.0; + } + + inline void clearImpulseComputed() { impulseComputed = false; } + + bool isIntersecting() { + RBXASSERT(geoPair.geoPairType == POINT_PLANE_PAIR); + return (contactPoint.length < -overlapGoal()); + } + + // Reorder the SimBody(s) so that simBody0 is always in kernel and adjust contact point data accordingly + bool getReordedSimBody(SimBody*& simBody0, SimBody*& simBody1, Body*& bodyNotInKernel, PairParams& params); + bool getReordedSimBody(SimBody*& simBody0, SimBody*& simBody1, PairParams& params); + + // Compute the relative velocities between the two bodies + bool getSimBodyAndContactVelocity(SimBody*& simBody0, SimBody*& simBody1, PairParams& params, + float& normalVel, Vector3& perpVel); + float computeRelativeVelocity(const PairParams ¶ms, Vector3* deltaVnormal, Vector3* perpVel); + float computeRelativeVelocity(); + + void applyContactPointForSymmetryDetection(SimBody* simBody0, SimBody* simBody1, + const PairParams& params, float direction); + + const ContactParams& getContactParams() const { return contactParams; } + void setContactParams(const ContactParams& params) { contactParams = params; } + + /////////// Called by kernel ////////////////////////////// + /* override*/ virtual void computeForce(bool throttling); + /* override*/ virtual bool computeImpulse(float& residualVelocity); + /* override*/ bool canThrottle() const; + + // Debug + static float percentActive(); + + float computeOverlap() { // positive == bigger overlap + updateContactPoint(); + return -contactPoint.length; + } + + inline PairParams& getContactPoint() { return contactPoint; } + inline const PairParams& getContactPoint() const { return contactPoint; } + + void getLengthNormalPosition(Vector3& position, Vector3& normal, float& length) { + position = contactPoint.position; + normal = contactPoint.normal; + length = contactPoint.length; + } + + inline bool isRestingContact() { return age > 4; } + }; + + class GeoPairConnector + : public ContactConnector + , public Allocator + { + public: + GeoPairConnector(Body* b0, Body* b1, const ContactParams& contactParams) : ContactConnector(b0, b1, contactParams) + {} + + ////////////////////////////////////////////////////////////////////////////////// + + /*override*/ void updateContactPoint() + { + geoPair.computeLengthNormalPosition(contactPoint); + ContactConnector::updateContactPoint(); + } + + void setPointPlane(const Vector3* oPoint, const Vector3* oPlane, + int pointId, NormalId planeId) + { + geoPair.setPointPlane(oPoint, oPlane, pointId, planeId); + } + + void setEdgeEdgePlane(const Vector3* e0, const Vector3* e1, + NormalId n0, NormalId n1, NormalId planeId, float edgeLength0, float edgeLength1) + { + geoPair.setEdgeEdgePlane(e0, e1, n0, n1, planeId, edgeLength0, edgeLength1); + } + + void setEdgeEdge(const Vector3* e0, const Vector3* e1, NormalId n0, NormalId n1) + { + geoPair.setEdgeEdge(e0, e1, n0, n1); + } + + bool match(Body* b0, Body* b1, GeoPairType pairType, int param0, int param1) + { + return geoPair.match(b0, b1, pairType, param0, param1); + } + }; + + //////////////////////////////////////////////////////////////////////////////// + + class BallBallConnector : public ContactConnector + , public Allocator + { + private: + float radius0; + float radiusSum; + + public: + BallBallConnector(Body* b0, Body* b1, const ContactParams& contactParams) + : ContactConnector(b0, b1, contactParams) + {} + + /*override*/ void updateContactPoint(); + void setRadius(float r0, float r1) { + radius0 = r0; + radiusSum = r0 + r1; + } + }; + + + //////////////////////////////////////////////////////////////////////////////// + + class BallBlockConnector : public ContactConnector, + public Allocator + { + private: + float radius0; // ball + Vector3 offset1; + NormalId normalId1; + GeoPairType geoPairType; + + void computeBallPoint(PairParams& params); + void computeBallEdge(PairParams& params); + void computeBallPlane(PairParams& params); + + public: + BallBlockConnector(Body* b0, Body* b1, const ContactParams& contactParams) + : ContactConnector(b0, b1, contactParams) + {} + + /*override*/ void updateContactPoint(); + void setBallBlock(float _radius0, const Vector3* _offset1, RBX::NormalId _normalID, GeoPairType _geoPairType) { + offset1 = *_offset1; + radius0 = _radius0; + normalId1 = _normalID; + geoPairType = _geoPairType; + } + }; + +} // namespace \ No newline at end of file diff --git a/App/v8kernel/ContactParams.h b/App/v8kernel/ContactParams.h new file mode 100644 index 0000000..97a667c --- /dev/null +++ b/App/v8kernel/ContactParams.h @@ -0,0 +1,44 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +namespace RBX { + + class ContactParams { + public: + float kSpring; // spring constant + float kNeg; // elastic - spring constant on bounceback + float kFriction; // contact only variable stored as true value * -0.5;??? + float kElasticity; + + ContactParams() + : kSpring(0.0) + , kFriction(0.0) + , kNeg(0.0) + , kElasticity(0.0f) + + {} + }; + + + enum GeoPairType { BALL_POINT_PAIR, // BALL to BLOCK + BALL_EDGE_PAIR, + BALL_PLANE_PAIR, + // BLOCK to BLOCK + POINT_PLANE_PAIR, + EDGE_EDGE_PLANE_PAIR, // two edges, plane needed to supply normal + EDGE_EDGE_PAIR, // two edges, guaranteed to be overlapping + + VERTEX_PLANE_CONNECTOR, + EDGE_EDGE_CONNECTOR, + EDGE_EDGE_PLANE_CONNECTOR, + + BALL_VERTEX_CONNECTOR, + BALL_EDGE_CONNECTOR, + BALL_PLANE_CONNECTOR, + + BULLET_SHAPE_CONNECTOR, + BULLET_SHAPE_CELL_CONNECTOR }; + + +} // namespace \ No newline at end of file diff --git a/App/v8kernel/Debug.h b/App/v8kernel/Debug.h new file mode 100644 index 0000000..24befe6 --- /dev/null +++ b/App/v8kernel/Debug.h @@ -0,0 +1,13 @@ +#pragma once + +#include "RBX/Debug.h" + +// Engine assertions often cause the game to stop running. +// Until we can fix these, turn them off. +//#define RBX_DEBUGENGINE + +#ifdef RBX_DEBUGENGINE +#define RBX_ENGINE_ASSERT(expr) RBXASSERT(expr) +#else +#define RBX_ENGINE_ASSERT(expr) ((void)0) +#endif \ No newline at end of file diff --git a/App/v8kernel/IStage.h b/App/v8kernel/IStage.h new file mode 100644 index 0000000..a8f36c5 --- /dev/null +++ b/App/v8kernel/IStage.h @@ -0,0 +1,74 @@ + /* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "rbx/Debug.h" +#include "Util/G3DCore.h" + +namespace RBX { + + class Kernel; + + class RBXBaseClass IStage { + public: + typedef enum { CLEAN_STAGE, + JOINT_STAGE, + GROUND_STAGE, + EDGE_STAGE, + CONTACT_STAGE, + TREE_STAGE, + MOVING_STAGE, + SPATIAL_FILTER, + MECH_TO_ASSEMBLY_STAGE, + ASSEMBLY_STAGE, + MOVING_ASSEMBLY_STAGE, + STEP_JOINTS_STAGE, + HUMANOID_STAGE, + SLEEP_STAGE, + SIMULATE_STAGE, + KERNEL_STAGE} StageType; + + private: + IStage* upstream; + IStage* downstream; + + const IStage* findStageImpl(StageType stageType) const { + const IStage* answer = this; + while (answer->getStageType() != stageType) { + answer = answer->getDownstream(); + } + return answer; + } + + public: + IStage(IStage* upstream, IStage* downstream) + : upstream(upstream), downstream(downstream) + {} + + virtual ~IStage() { + if (downstream) { + delete downstream; + } + } + + IStage* getUpstream() {return upstream;} + IStage* getDownstream() {return downstream;} + const IStage* getDownstream() const {return downstream;} + + virtual StageType getStageType() const = 0; + + const IStage* findStage(StageType stageType) const { + return findStageImpl(stageType); + } + + IStage* findStage(StageType stageType) { + return const_cast(findStageImpl(stageType)); + } + + virtual Kernel* getKernel() { + RBXASSERT(downstream); + return downstream->getKernel(); + } + }; + +} // namespace \ No newline at end of file diff --git a/App/v8kernel/Kernel.h b/App/v8kernel/Kernel.h new file mode 100644 index 0000000..11d23df --- /dev/null +++ b/App/v8kernel/Kernel.h @@ -0,0 +1,128 @@ +#pragma once + +#include "V8Kernel/IStage.h" +#include "V8Kernel/BodyPvSetter.h" +#include "boost/scoped_ptr.hpp" +#include "solver/Solver.h" + +namespace RBX { + + namespace Profiling + { + class CodeProfiler; + } + + class Connector; + class Body; + class Point; + class KernelData; + + class Kernel : public IStage, + public BodyPvSetter + { + private: + static int numKernels; + int maxBodies; + bool inStepCode; + int numLastIterations; + int numOfMaxIterations; + float error; + float maxError; + bool validateBody(Body* b); + bool validateConnector(Connector* connector) const; + bool validateConnectorBody(Body* b) const; + + KernelData* kernelData; + + // Funny Physics + void stepWorldFunnyPhysics(int worldStepId); + void stepFunnyPhysics(const Vector3& move); + void stepFunnyPhysicsBody(Body* b, const Vector3& move); + + Point* searchForDuplicatePoint(Point* tempPoint); + + void preStep(); + void preStepThrottled(); + void stepWorld( boost::uint64_t distDebugTime ); + void stepWorldThrottled( boost::uint64_t debugTime ); + + bool usingPGSSolver; + + public: + PGSSolver pgsSolver; + Kernel(IStage* upstream); + ~Kernel(); + + /////////////////////////////////////////// + // IStage + /*override*/ IStage::StageType getStageType() const {return IStage::KERNEL_STAGE;} + + /*override*/ Kernel* getKernel() {return this;} + + void step(bool throttling, int numThreads, boost::uint64_t debugTime); + + void insertBody(Body* b); + void insertPoint(Point* p); + void insertConnector(Connector* c); + + void removeBody(Body* b); + void removePoint(Point* p); + void removeConnector(Connector* c); + + // double up on points if same body, position.... + // TODO: move this to the point class - reference counted pointer + // + Point* newPointLocal(class Body* _body, const Vector3& worldPos); + Point* newPoint(class Body* _body, const Vector3& worldPos); + void deletePoint(class Point* point); + + //////////////////////////////////////////////////////// + // + // Debugging Stuff + // + void report(); // system energy to log file + static void reportMemorySizes(); + + float connectorSpringEnergy() const; + float bodyPotentialEnergy() const; + float bodyKineticEnergy() const; + float totalEnergy() const { + return connectorSpringEnergy() + bodyPotentialEnergy() + bodyKineticEnergy(); + } + float totalKineticEnergy() const { + return connectorSpringEnergy() + bodyKineticEnergy(); + } + + int numFreeFallBodies() const; + int numRealTimeBodies() const; + int numJointBodies() const; + int numContactBodies() const; + int numBodies() const {return numFreeFallBodies() + numRealTimeBodies() + numJointBodies() + numContactBodies();} + int numBodiesMax() const {return maxBodies;} + int numLeafBodies() const; + int numPoints() const; + int numConnectors() const; + int numHumanoidConnectors() const; + int numRealTimeConnectors() const; + int numSecondPassConnectors() const; + int numJointConnectors() const; + int numBuoyancyConnectors() const; + int numContactConnectors() const; + + inline int numIterations() const {return numLastIterations;} + inline int numMaxIterations() const {return numOfMaxIterations;} + inline float getSolverError() const {return error;} + inline float getMaxSolverError() const {return maxError;} + int fakeDeceptiveSolverIterations() const; + int fakeDeceptiveMatrixSize() const; + + /////////////////////////////////////////// + // Profiler + boost::scoped_ptr profilingKernelBodies; + boost::scoped_ptr profilingKernelConnectors; + + void setUsingPGSSolver(bool pgsOn) { usingPGSSolver = pgsOn; } + bool getUsingPGSSolver() const { return usingPGSSolver; } + void dumpLog( bool enable ) { pgsSolver.dumpLog( enable ); } + }; +} // namespace diff --git a/App/v8kernel/KernelData.h b/App/v8kernel/KernelData.h new file mode 100644 index 0000000..6388c36 --- /dev/null +++ b/App/v8kernel/KernelData.h @@ -0,0 +1,380 @@ +#pragma once + +#include "v8kernel/Body.h" +#include "V8Kernel/SimBody.h" +#include "V8Kernel/Point.h" +#include "V8Kernel/Connector.h" +#include "V8Kernel/ContactConnector.h" +#include "V8Kernel/BuoyancyConnector.h" +#include "V8kernel/Constants.h" +#include "V8datamodel/FastLogSettings.h" + + +namespace RBX { + + class KernelData { + public: + // main object types + IndexArray freeFallBodies; // bodies with no connectors + IndexArray realTimeBodies; // humanoid bodies that are not throttle-able + IndexArray jointBodies; // bodies with joint connectors + IndexArray contactBodies; // bodies with contact connectors but no joint connectors + IndexArray leafBodies; // need update PV every step, NOT in kernel! + IndexArray points; + IndexArray humanoidConnectors; // The humanoids + IndexArray secondPassConnectors; // kernel joints + IndexArray realTimeConnectors; // connectors on humanoid body parts + IndexArray jointConnectors; + IndexArray buoyancyConnectors; + IndexArray contactConnectors; + + KernelData() + { + } + + ~KernelData() { + RBXASSERT(freeFallBodies.size() == 0); + RBXASSERT(realTimeBodies.size() == 0); + RBXASSERT(jointBodies.size() == 0); + RBXASSERT(contactBodies.size() == 0); + RBXASSERT(leafBodies.size() == 0); + RBXASSERT(points.size() == 0); + RBXASSERT(humanoidConnectors.size() == 0); + RBXASSERT(secondPassConnectors.size() == 0); + RBXASSERT(realTimeConnectors.size() == 0); + RBXASSERT(jointConnectors.size() == 0); + RBXASSERT(buoyancyConnectors.size() == 0); + RBXASSERT(contactConnectors.size() == 0); + } + + inline void addLeafBodies(Body* b) + { + for (int i = 0; i < b->numChildren(); ++i) + { + Body* child = b->getChild(i); + RBXASSERT(!child->isLeafBody()); + RBXASSERT(child != child->getRoot()); + if (child->connectorUseCount > 0) + { + addLeafBody(child); + } + addLeafBodies(child); + } + } + + inline void insertBody(Body* b) + { + RBXASSERT(b->getRoot() == b); + SimBody* simBody = b->getSimBody(); + RBXASSERT(!b->isLeafBody() && !simBody->isInKernel()); + addBodyToNewList(simBody); + RBXASSERT(simBody->validateBodyLists()); + } + + inline void removeBody(Body* b) + { + RBXASSERT(b->getRoot() == b); + RBXASSERT(!b->isLeafBody()); + + SimBody* simBody = b->getSimBody(); + removeBodyFromCurrentList(simBody); + simBody->clearSymStateAndAccummulator(); + RBXASSERT(simBody->validateBodyLists()); + } + + inline void addConnector(Connector* c, bool pgsOn) + { + RBXASSERT(!c->isInKernel()); + Body* body0 = c->getBody(Connector::body0); + Body* body1 = c->getBody(Connector::body1); + SimBody* simBody0 = body0 ? body0->getRootSimBody() : NULL; + SimBody* simBody1 = body1 ? body1->getRootSimBody() : NULL; + + Connector::KernelType connectorType = c->getConnectorKernelType(); + if ((simBody0 == NULL || !simBody0->isInKernel()) && + (simBody1 == NULL || !simBody1->isInKernel()) && + connectorType != Connector::HUMANOID && + connectorType != Connector::JOINT && + connectorType != Connector::KERNEL_JOINT) + return; + + if (connectorType == Connector::HUMANOID) + { + humanoidConnectors.fastAppend(c); + if (simBody0) + simBody0->incrementHumanoidConnectorCount(); + if (simBody1) + simBody1->incrementHumanoidConnectorCount(); + } + else if (connectorType == Connector::KERNEL_JOINT) + { + secondPassConnectors.fastAppend(c); + if (simBody0) + simBody0->incrementSecondPassConnectorCount(); + if (simBody1) + simBody1->incrementSecondPassConnectorCount(); + } + else if (pgsOn && connectorType == Connector::JOINT) + { + jointConnectors.fastAppend(c); + if (simBody0) + simBody0->incrementJointConnetorCount(); + if (simBody1) + simBody1->incrementJointConnetorCount(); + } + else if (pgsOn && connectorType == Connector::BUOYANCY) + { + buoyancyConnectors.fastAppend(c); + if (simBody0) + simBody0->incrementBuoyancyConnectorCount(); + if (simBody1) + simBody1->incrementBuoyancyConnectorCount(); + } + else if ((simBody0 && !simBody0->getBody()->getCanThrottle()) || + (simBody1 && !simBody1->getBody()->getCanThrottle())) + { + realTimeConnectors.fastAppend(c); + if (simBody0) + simBody0->incrementRealTimeConnectorCount(); + if (simBody1) + simBody1->incrementRealTimeConnectorCount(); + } + else if (!pgsOn && + (connectorType == Connector::JOINT || + connectorType == Connector::BUOYANCY || + ((simBody0 && simBody0->isJointBody()) || // Contact connectors in touch with joint bodies + (simBody1 && simBody1->isJointBody())))) // are considered joint connectors. + { + jointConnectors.fastAppend(c); + if (simBody0) + simBody0->incrementJointConnetorCount(); + if (simBody1) + simBody1->incrementJointConnetorCount(); + } + else + { + RBXASSERT(connectorType == Connector::CONTACT); + contactConnectors.fastAppend(c); + if (simBody0) + simBody0->incrementContactConnectorCount(); + if (simBody1) + simBody1->incrementContactConnectorCount(); + } + + if (body0) + addConnectorToBody(c, body0); + if (body1) + addConnectorToBody(c, body1); + + RBXASSERT(simBody0 == NULL || simBody0->validateBodyLists()); + RBXASSERT(simBody1 == NULL || simBody1->validateBodyLists()); + } + + inline void removeConnector(Connector* c) + { + if (!c->isInKernel()) + return; + + Body* body0 = c->getBody(Connector::body0); + Body* body1 = c->getBody(Connector::body1); + SimBody* simBody0 = body0 ? body0->getRootSimBody() : NULL; + SimBody* simBody1 = body1 ? body1->getRootSimBody() : NULL; + + if (c->isHumanoid()) + { + humanoidConnectors.fastRemove(c); + if (simBody0) + simBody0->decrementHumanoidConnectorCount(); + if (simBody1) + simBody1->decrementHumanoidConnectorCount(); + } + else if (c->isSecondPass()) + { + secondPassConnectors.fastRemove(c); + if (simBody0) + simBody0->decrementSecondPassConnectorCount(); + if (simBody1) + simBody1->decrementSecondPassConnectorCount(); + } + else if (c->isRealTime()) + { + realTimeConnectors.fastRemove(c); + if (simBody0) + simBody0->decrementRealTimeConnectorCount(); + if (simBody1) + simBody1->decrementRealTimeConnectorCount(); + } + else if (c->isJoint()) + { + jointConnectors.fastRemove(c); + if (simBody0) + simBody0->decrementJointConnetorCount(); + if (simBody1) + simBody1->decrementJointConnetorCount(); + } + else if (c->isBuoyancy()) + { + buoyancyConnectors.fastRemove(c); + if (simBody0) + simBody0->decrementBuoyancyConnectorCount(); + if (simBody1) + simBody1->decrementBuoyancyConnectorCount(); + } + else + { + RBXASSERT(c->isContact()); + contactConnectors.fastRemove(c); + if (simBody0) + simBody0->decrementContactConnectorCount(); + if (simBody1) + simBody1->decrementContactConnectorCount(); + + ContactConnector* conn = static_cast(c); + PairParams params = conn->getContactPoint(); + if (conn->getReordedSimBody(simBody0, simBody1, params)) + conn->applyContactPointForSymmetryDetection(simBody0, simBody1, params, -1.0f); + } + + if (body0) + removeConnectorFromBody(c, body0); + if (body1) + removeConnectorFromBody(c, body1); + + RBXASSERT(simBody0 == NULL || simBody0->validateBodyLists()); + RBXASSERT(simBody1 == NULL || simBody1->validateBodyLists()); + } + + private: + + inline void addLeafBody(Body* b) + { + RBXASSERT(!b->isLeafBody()); + RBXASSERT(b->connectorUseCount > 0); + leafBodies.fastAppend(b); + RBXASSERT(b->isLeafBody()); + } + + inline void removeLeafBody(Body* b) + { + RBXASSERT(b->isLeafBody()); + leafBodies.fastRemove(b); + RBXASSERT(!b->isLeafBody()); + } + + inline void removeLeafBodies(Body* b) + { + for (int i = 0; i < b->numChildren(); ++i) + { + Body* child = b->getChild(i); + if (child->isLeafBody()) + { + removeLeafBody(child); + } + removeLeafBodies(child); + } + } + + inline void removeBodyFromCurrentList(SimBody* simBody) + { + if (simBody->isRealTimeBody()) + { + removeLeafBodies(simBody->getBody()); + realTimeBodies.fastRemove(simBody); + } else if (simBody->isJointBody()) + { + removeLeafBodies(simBody->getBody()); + jointBodies.fastRemove(simBody); + } else if (simBody->isContactBody()) + { + contactBodies.fastRemove(simBody); + } else if (simBody->isFreeFallBody()) + { + freeFallBodies.fastRemove(simBody); + simBody->updateAngMomentum(); + } else + return; + simBody->setDt(0.0f); + } + + inline void addBodyToNewList(SimBody* simBody) + { + if (!simBody->getBody()->getCanThrottle()) + { + if (!simBody->isRealTimeBody()) + { + removeBodyFromCurrentList(simBody); + realTimeBodies.fastAppend(simBody); + simBody->setDt(Constants::kernelDt()); + addLeafBodies(simBody->getBody()); + } + } else if (simBody->getJointConnectorCount() > 0) + { + if (!simBody->isJointBody()) + { + removeBodyFromCurrentList(simBody); + jointBodies.fastAppend(simBody); + simBody->setDt(Constants::kernelDt()); + addLeafBodies(simBody->getBody()); + } + } else if (simBody->getContactConnectorCount() > 0) + { + if (!simBody->isContactBody()) + { + removeBodyFromCurrentList(simBody); + contactBodies.fastAppend(simBody); + simBody->setDt(Constants::freeFallDt()); + } + } else + { + if (simBody->getConnectorCount() == 0) + { + if (!simBody->isFreeFallBody()) + { + removeBodyFromCurrentList(simBody); + freeFallBodies.fastAppend(simBody); + simBody->setDt(Constants::freeFallDt()); + simBody->clearSymStateAndAccummulator(); + } + } else + { + if (!simBody->isJointBody()) + { + removeBodyFromCurrentList(simBody); + jointBodies.fastAppend(simBody); + simBody->setDt(Constants::kernelDt()); + addLeafBodies(simBody->getBody()); + } + } + } + } + + inline void addConnectorToBody(Connector* c, Body* body) + { + body->connectorUseCount++; + SimBody* simBody = body->getRootSimBody(); + + if (!simBody->isInKernel()) + return; + addBodyToNewList(simBody); + + // adds leaf bodies if root is already here and it is subject to the spring solver + if (body != body->getRoot() && !body->isLeafBody() && + (simBody->isJointBody() || simBody->isRealTimeBody())) + addLeafBody(body); + } + + inline void removeConnectorFromBody(Connector* c, Body* body) + { + body->connectorUseCount--; + + if (body->isLeafBody() && body->connectorUseCount == 0) + removeLeafBody(body); + + SimBody* simBody = body->getRootSimBody(); + if (!simBody->isInKernel()) + return; + addBodyToNewList(simBody); + } + }; + +} // namespace diff --git a/App/v8kernel/KernelIndex.h b/App/v8kernel/KernelIndex.h new file mode 100644 index 0000000..e0d47a6 --- /dev/null +++ b/App/v8kernel/KernelIndex.h @@ -0,0 +1,26 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "rbx/Debug.h" + +namespace RBX { + + class KernelIndex { + protected: + int kernelIndex; + + public: + bool indexInKernel() const { + return (kernelIndex != -1); + } + + KernelIndex() : kernelIndex(-1) + {} + + ~KernelIndex() { + RBXASSERT(!indexInKernel()); + } + }; + +} // namespace \ No newline at end of file diff --git a/App/v8kernel/Link.h b/App/v8kernel/Link.h new file mode 100644 index 0000000..05a0e45 --- /dev/null +++ b/App/v8kernel/Link.h @@ -0,0 +1,86 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/G3DCore.h" +#include "rbx/Declarations.h" +#include "Util/Memory.h" +#include "Util/Math.h" + +namespace RBX { + + class Body; + + class RBXBaseClass Link + { + friend class Body; + + protected: + Body* body; // body I'm affilliated with (child) + CoordinateFrame parentCoord; + CoordinateFrame childCoord; + CoordinateFrame childCoordInverse; + + CoordinateFrame childInParent; + unsigned int stateIndex; + + virtual void computeChildInParent(CoordinateFrame& answer) const = 0; + + void dirty(); + + void setBody(Body* b) {body = b;} + + public: + Link(); + + ~Link(); + + const CoordinateFrame& getChildInParent(); + + Body* getBody() const {return body;} + + void reset( + const CoordinateFrame& parentC, + const CoordinateFrame& childC); + }; + + + class RevoluteLink + : public Link + , public Allocator + { + private: + float jointAngle; + + /*override*/ void computeChildInParent(CoordinateFrame& answer) const; + + public: + RevoluteLink() : jointAngle(0.0f) + { + } + + void setJointAngle(float value) { + jointAngle = value; + dirty(); + } + }; + + class D6Link + : public Link + , public Allocator + { + private: + CoordinateFrame offsetCFrame; + + /*override*/ void computeChildInParent(CoordinateFrame& answer) const; + + public: + void setJointOffsetCFrame(const CoordinateFrame& value) { + offsetCFrame = value; + RBXASSERT(!Math::hasNanOrInf(value)); + dirty(); + } + }; + +} // namespace + diff --git a/App/v8kernel/Pair.h b/App/v8kernel/Pair.h new file mode 100644 index 0000000..4a1cefa --- /dev/null +++ b/App/v8kernel/Pair.h @@ -0,0 +1,148 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Kernel/ContactParams.h" +#include "Util/NormalID.h" +#include "Util/G3DCore.h" +#include "rbx/Debug.h" + +namespace RBX { + + class Body; + + class PairParams { + public: + Vector3 normal; + union { + float length; + float rotation; + }; + Vector3 position; + PairParams() { + normal = position = Vector3::zero(); + length = 0.0f; + } + bool operator==(const PairParams& other) { + return (length == other.length && position == other.position && normal == other.normal); + } + }; + + /////////////////////////////////////////////////////////////////// + + class GeoPair + { + public: + GeoPairType geoPairType; + + // note, pair point0 and point1 have the following polarity + // ball ball: radius0 == point0 body + // ball block: ball->point0, block->point1 + // point plane: pointBlock->0, planeBlock->1 + // edge edge plane: planeBlock->1 + // fixed, not allocated here + + // This is defining data + Vector3 offset0; + Vector3 offset1; + Body* body0; + Body* body1; + float edgeLength0; + float edgeLength1; + struct { + union { + RBX::NormalId normalID0; + float radius0; }; + union { + RBX::NormalId normalID1; + float radiusSum; }; + union { + RBX::NormalId planeID;// edge/edge/plane coords - the normal from the plane + int point0ID; }; + } pairData; + + private: + void computePointPlane(PairParams& _params); + void computeEdgeEdgePlane(PairParams& _params); + void computeEdgeEdgePlane2(PairParams& _params); + void computeEdgeEdge(PairParams& _params); + + public: + GeoPair(); + + ////////// Kernel Update + + inline void computeLengthNormalPosition(PairParams& _params) + { + switch (geoPairType) { + case (POINT_PLANE_PAIR): computePointPlane(_params); break; + case (EDGE_EDGE_PLANE_PAIR): computeEdgeEdgePlane2(_params); break; + case (EDGE_EDGE_PAIR): computeEdgeEdge(_params); break; + default: RBXASSERT(0); + } + } + + ///////////////// GeoPair geometric functions + + void setPointPlane(const Vector3* _offsetPoint, + const Vector3* _offsetPlane, int _pointID, RBX::NormalId _planeNormalID) { + offset0 = *_offsetPoint; + offset1 = *_offsetPlane; + pairData.point0ID = _pointID; // purely here for the match + pairData.normalID1 = _planeNormalID; + geoPairType = POINT_PLANE_PAIR; + } + + void setEdgeEdgePlane(const Vector3* _edge0, const Vector3* _edge1, + RBX::NormalId _normal0, RBX::NormalId _normal1, RBX::NormalId _planeID, float _edgeLength0, float _edgeLength1) { + offset0 = *_edge0; + offset1 = *_edge1; + pairData.normalID0 = _normal0; + pairData.normalID1 = _normal1; + pairData.planeID = _planeID; + edgeLength0 = _edgeLength0; + edgeLength1 = _edgeLength1; + geoPairType = EDGE_EDGE_PLANE_PAIR; + } + + void setEdgeEdge(const Vector3* _edge0, const Vector3* _edge1, + RBX::NormalId _normal0, RBX::NormalId _normal1) { + offset0 = *_edge0; + offset1 = *_edge1; + pairData.normalID0 = _normal0; + pairData.normalID1 = _normal1; + geoPairType = EDGE_EDGE_PAIR; + } + + bool match(Body* _b0, Body* _b1, GeoPairType _pairType, int param0, int param1) { + if (_pairType == POINT_PLANE_PAIR) { + return ( (_b0 == body0) + && (_b1 == body1) + && (param0 == pairData.point0ID) + && (param1 == pairData.normalID1) ); + } + + else if (_pairType == EDGE_EDGE_PLANE_PAIR) { + return ( (_b0 == body0) + && (_b1 == body1) + && (param0 == pairData.normalID0) + && (param1 == pairData.normalID1) ); + } + + else { + RBXASSERT(_pairType == EDGE_EDGE_PAIR); + return ( ( (_b0 == body0) + && (_b1 == body1) + && (param0 == pairData.normalID0) + && (param1 == pairData.normalID1) ) + || + ( (_b0 == body1) + && (_b1 == body0) + && (param0 == pairData.normalID1) + && (param1 == pairData.normalID0) ) + ); + } + } + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/v8kernel/Point.h b/App/v8kernel/Point.h new file mode 100644 index 0000000..ddf4db9 --- /dev/null +++ b/App/v8kernel/Point.h @@ -0,0 +1,83 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Kernel/KernelIndex.h" +#include "Util/G3DCore.h" + +namespace RBX { + + class Body; + + + class Point + : public KernelIndex + { + friend class KernelData; + friend class Kernel; + private: + int& getKernelIndex() {return kernelIndex;} + int numOwners; + + protected: + Body* body; + + // constant + Vector3 localPos; + + + + + + // auxillary variables, computed on every frame + Vector3 worldPos; + + // accumulated quantities; + Vector3 force; + + // This is private - only created by the kernel + Point(Body* _body = NULL); + + virtual ~Point() + {} + + public: // all points from same allocator, size of AttachPoint + + static bool sameBodyAndOffset(const Point& p0, const Point& p1) { + return ((p0.body == p1.body) && (p0.localPos == p1.localPos)); + } + + //////////// called by kernel every step + // + // Updates World Position, Clears Accumulator + + void step(); + + // force accumulation + void accumulateForce(const Vector3& _force) { + force += _force; + } + + // corresponds to "for each Point, accumulate forces to Body" + void forceToBody(); + + void setLocalPos(const Vector3& _localPos); + + void setWorldPos(const Vector3& _worldPos); + + void setBody(Body* _body) { + body = _body; + } + + //////////// inquiry + Body* getBody() { + return body; + } + + const Vector3& getWorldPos() { + return worldPos; + } + }; + +} // namespace RBX + diff --git a/App/v8kernel/PolyConnectors.h b/App/v8kernel/PolyConnectors.h new file mode 100644 index 0000000..6efd35c --- /dev/null +++ b/App/v8kernel/PolyConnectors.h @@ -0,0 +1,218 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Kernel/ContactParams.h" +#include "V8Kernel/ContactConnector.h" +#include "Util/G3DCore.h" +#include "rbx/Debug.h" + +namespace RBX { + + ////////////////////////////////////////////////////////////////////////////////// + // + // BLOCK BLOCK Types + + class PolyConnector : public ContactConnector + { + private: + int param0; + int param1; // for matching + + protected: + /*implement*/ virtual GeoPairType getConnectorType() const = 0; + + PolyConnector( + Body* b0, + Body* b1, + const ContactParams& contactParams, + int param0, + int param1) + : ContactConnector(b0, b1, contactParams) + , param0(param0) + , param1(param1) + {} + + public: + static bool match(PolyConnector* p0, PolyConnector* p1) { + return ( (p0->param0 == p1->param0) + && (p0->param1 == p1->param1) + && (p0->getConnectorType() == p1->getConnectorType()) ); + } + }; + + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + + class FaceVertexConnector : public PolyConnector, + public Allocator + { + private: + Plane facePlane; + Vector3 vertexOffset; + + /*override*/ GeoPairType getConnectorType() const {return VERTEX_PLANE_CONNECTOR;} + + public: + FaceVertexConnector( + Body* b0, + Body* b1, + const ContactParams& contactParams, + const Plane& facePlane, + const Vector3& vertexOffset, + int planeId, + int vertexId) + : PolyConnector(b0, b1, contactParams, planeId, vertexId) + , facePlane(facePlane) + , vertexOffset(vertexOffset) + {} + + /*override*/ void updateContactPoint(); + }; + + + class FaceEdgeConnector : public PolyConnector, + public Allocator + { + private: + Plane facePlane; + Plane sideFacePlane; + Line faceLine; + Line edgeLine; + + /*override*/ GeoPairType getConnectorType() const {return EDGE_EDGE_PLANE_CONNECTOR;} + + public: + FaceEdgeConnector( + Body* b0, + Body* b1, + const ContactParams& contactParams, + const Plane& facePlane, + const Plane& sideFacePlane, + Line faceLine, + Line edgeLine, + const int faceId, + const int edgeId) + : PolyConnector(b0, b1, contactParams, faceId, edgeId) + , facePlane(facePlane) + , sideFacePlane(sideFacePlane) + , faceLine(faceLine) + , edgeLine(edgeLine) + {} + + /*override*/ void updateContactPoint(); + }; + + class EdgeEdgeConnector : public PolyConnector, + public Allocator + { + private: + Line edgeLine0; + Line edgeLine1; + + /*override*/ GeoPairType getConnectorType() const {return EDGE_EDGE_CONNECTOR;} + + public: + EdgeEdgeConnector( + Body* b0, + Body* b1, + const ContactParams& contactParams, + Line edgeLine0, + Line edgeLine1, + int edgeId0, + int edgeId1 ) + : PolyConnector(b0, b1, contactParams, edgeId0, edgeId1) + , edgeLine0(edgeLine0) + , edgeLine1(edgeLine1) + { + } + + /*override*/ void updateContactPoint(); + }; + + + //////////////////////////////////////////////////////////////////////////////// + + class BallVertexConnector : public PolyConnector, + public Allocator + { + private: + float radius; + Vector3 offset; + + /*override*/ GeoPairType getConnectorType() const {return BALL_VERTEX_CONNECTOR;} + + public: + BallVertexConnector( + Body* b0, + Body* b1, + const ContactParams& contactParams, + float radius, + const Vector3& offset, + int vertexId) + : PolyConnector(b0, b1, contactParams, 0, vertexId) + , radius(radius) + , offset(offset) + {} + + /*override*/ void updateContactPoint(); + }; + + class BallEdgeConnector : public PolyConnector, + public Allocator + { + private: + float radius; + Vector3 offset; + Vector3 normal; + + /*override*/ GeoPairType getConnectorType() const {return BALL_EDGE_CONNECTOR;} + + public: + BallEdgeConnector( + Body* b0, + Body* b1, + const ContactParams& contactParams, + float radius, + const Vector3& offset, + const Vector3& normal, + int edgeId) + : PolyConnector(b0, b1, contactParams, 0, edgeId) + , radius(radius) + , offset(offset) + , normal(normal) + {} + + /*override*/ void updateContactPoint(); + }; + + class BallPlaneConnector : public PolyConnector, + public Allocator + { + private: + float radius; + Vector3 offset; + Vector3 normal; + + /*override*/ GeoPairType getConnectorType() const {return BALL_PLANE_CONNECTOR;} + + public: + BallPlaneConnector( + Body* b0, + Body* b1, + const ContactParams& contactParams, + float radius, + const Vector3& offset, + const Vector3& normal, + int faceId) + : PolyConnector(b0, b1, contactParams, 0, faceId) + , radius(radius) + , offset(offset) + , normal(normal) + {} + + /*override*/ void updateContactPoint(); + }; + +} // namespace RBX \ No newline at end of file diff --git a/App/v8kernel/SimBody.h b/App/v8kernel/SimBody.h new file mode 100644 index 0000000..33fc929 --- /dev/null +++ b/App/v8kernel/SimBody.h @@ -0,0 +1,272 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/G3DCore.h" +#include "Util/PV.h" +#include "Util/Quaternion.h" +#include "Util/Math.h" +#include "Util/Memory.h" +#include "rbx/threadsafe.h" +#include "v8kernel/Constants.h" +#include "Fastlog.h" + +namespace RBX { + + class Body; + class SimBody + : public Allocator + { + private: + Body* body; + float dt; + bool dirty; + boost::uint64_t uid; + + PV pv; + Quaternion qOrientation; // master for simulation + Vector3 angMomentum; // master for simulation + Vector3 moment; + Vector3 momentRecip; + Matrix3 momentRecipWorld; + float massRecip; + float constantForceY; + + // accumulators + Vector3 force; // at center of mass, in world coordinates + Vector3 torque; // in world coordinates + Vector3 impulse; // at center of mass, in world coordinates + Vector3 rotationalImpulse; // in world coordinates + + // Cache for impulse solver + Vector3 impulseLast; + + int freeFallBodyIndex; + int realTimeBodyIndex; + int jointBodyIndex; + int buoyancyBodyIndex; + int contactBodyIndex; + + int numOfConnectors; // how many connectors connect this SimBody (assembly) + int numOfHumanoidConnectors; + int numOfSecondPassConnectors; + int numOfRealTimeConnectors; + int numOfJointConnectors; + int numOfBuoyancyConnectors; + int numOfContactConnectors; + + // Symmetrical state detection + bool symmetricContact; + bool verticalContact; + Vector3 penetrationTorque; // aggregated from contact normals scaled by penetration depth + Vector3 penetrationForce; // aggregated from contact normals scaled by penetration depth + + inline void clearForceAccumulators() { + force = getWorldGravityForce(); + torque = Vector3(0.0, 0.0, 0.0); + } + + inline void clearImpulseAccumulators() { + impulse = Vector3(0.0, 0.0, 0.0); + rotationalImpulse = Vector3(0.0, 0.0, 0.0); + } + + + void update(); + + // All debugging stuff; + static float maxTorqueXX; + static float maxForceXX; + static float maxLinearImpulseXX; + static float maxRotationalImpulseXX; + static float maxDebugTorque(); + static float maxDebugForce(); + static float maxDebugLinearImpulse(); + static float maxDebugRotationalImpulse(); + + public: + SimBody(Body* body); + ~SimBody(); + + Body* getBody() {return body;} + const Body* getBodyConst() const {return body;} + void setDt(float _dt) {dt = _dt;} + float getDt() const {return dt;} + void setUID( boost::uint64_t _uid ) { uid = _uid; } + boost::uint64_t getUID() const { return uid; } + inline void updateMomentRecipWorld(); + inline Vector3 computeRotationVelocityFromMomentum(); + inline Vector3 computeRotationVelocityFromMomentumFast(); + inline const Matrix3& getInverseInertiaInWorld() const {return momentRecipWorld;} + + void step(); + void stepVelocity(); + void stepPosition(); + void stepFreeFall(); + + void applyImpulse(const Vector3& _impulse, const Vector3& worldPos); + + void clearVelocity(); + void updateAngMomentum(); + + void updateFromSolver( const Vector3& newPosition, const Matrix3& newOrientation, const Vector3& newLinearVelocity, const Vector3& newAngularVelocity ); + + inline void updateIfDirty() { // called before step. Assumes body cofm is clean + if (dirty) + update(); + } + + inline Vector3 getWorldGravityForce() const { return Vector3(0, constantForceY, 0); } + inline void clearSymStateAndAccummulator() { + symmetricContact = true; + verticalContact = true; + penetrationTorque = Vector3::zero(); + penetrationForce = Vector3::zero(); + } + + inline void makeDirty() {dirty = true;} + + bool getDirty() const {return dirty;} + + inline const PV& getPV() const {return pv;} + + PV getOwnerPV(); + + static Vector3 computeTorqueFromOffsetForce(const Vector3& _force, const Vector3& cofm, const Vector3& forceLocationWorld) { + Vector3 localPosWorld = forceLocationWorld - cofm; + return localPosWorld.cross(_force); + } + + ////////////////////////////////////////////////////////////////////////////// + // + // Parallel physics will accumulate forces from different threads - need a mutex for each + // + // + inline void accumulateForceCofm(const Vector3& _force) { + updateIfDirty(); + force += _force; + RBXASSERT_SLOW(force.isFinite()); + RBXASSERT_SLOW(Math::longestVector3Component(force) < maxDebugForce()); + } + + inline void accumulateForce(const Vector3& _force, const Vector3& worldPos) { + RBXASSERT_SLOW(Math::longestVector3Component(_force) < maxDebugForce()); + updateIfDirty(); + force += _force; + torque += computeTorqueFromOffsetForce(_force, pv.position.translation, worldPos); + RBXASSERT_SLOW(force.isFinite()); + RBXASSERT_SLOW(torque.isFinite()); + } + + inline void accumulatePenetrationForce(const Vector3& _force, const Vector3& worldPos) { + penetrationForce += _force; + penetrationTorque += computeTorqueFromOffsetForce(_force, pv.position.translation, worldPos); + } + + inline void accumulateTorque(const Vector3& _torque) { + RBXASSERT_SLOW(Math::longestVector3Component(_torque) < maxDebugTorque()); + updateIfDirty(); + torque += _torque; + RBXASSERT_SLOW(torque.isFinite()); + } + + inline void accumulateImpulse(const Vector3& _impulse, const Vector3& worldPos) { + RBXASSERT_SLOW(Math::longestVector3Component(_impulse) < maxDebugLinearImpulse()); + updateIfDirty(); + impulse += _impulse; + Vector3 localPosWorld = worldPos - pv.position.translation; + rotationalImpulse += localPosWorld.cross(_impulse); + RBXASSERT_SLOW(impulse.isFinite()); + RBXASSERT_SLOW(rotationalImpulse.isFinite()); + } + + inline void accumulateImpulseAtBranchCofm(const Vector3& _impulse) { + RBXASSERT_SLOW(Math::longestVector3Component(_impulse) < maxDebugLinearImpulse()); + updateIfDirty(); + impulse += _impulse; + RBXASSERT_SLOW(impulse.isFinite()); + } + + inline void accumulateRotationalImpulse(const Vector3& _rotationalImpulse) { + RBXASSERT_SLOW(Math::longestVector3Component(_rotationalImpulse) < maxDebugRotationalImpulse()); + updateIfDirty(); + rotationalImpulse += _rotationalImpulse; + RBXASSERT_SLOW(rotationalImpulse.isFinite()); + } + // End of parallel section + // + ////////////////////////////////////////////////////////////////////////////////////////// + + inline void resetImpulseAccumulators() { + updateIfDirty(); + clearImpulseAccumulators(); + } + + inline void resetForceAccumulators() { + updateIfDirty(); + clearForceAccumulators(); + } + + inline const Vector3& getForce() const {return force;} + + inline const Vector3& getTorque() const {return torque;} + + inline const Vector3& getImpulse() const {return impulse;} + + inline const Vector3& getRotationallmpulse() const {return rotationalImpulse;} + + inline const float& getMassRecip() const {return massRecip;} + + inline const Vector3& getImpulseLast() const {return impulseLast;} + + inline bool hasExternalForceOrImpulse() const {return force != getWorldGravityForce() || torque != Vector3::zero() || + impulse != Vector3::zero() || rotationalImpulse != Vector3::zero();} + + inline bool updateSymmetricContactState() { + symmetricContact = (penetrationTorque.squaredMagnitude() < Constants::impulseSolverSymStateTorqueBound()); + verticalContact = ( ( fabs(penetrationForce.x) < Constants::impulseSolverSymStateForceBound() ) && + ( fabs(penetrationForce.z) < Constants::impulseSolverSymStateForceBound() ) ); + + return symmetricContact; + } + + inline bool isSymmetricContact() const {return symmetricContact;} + inline bool isVerticalContact() const {return verticalContact;} + inline void clearSymmetricContact() {symmetricContact = false;} + inline int& getRealTimeBodyIndex() {return realTimeBodyIndex;} + inline int& getFreeFallBodyIndex() {return freeFallBodyIndex;} + inline int& getJointBodyIndex() {return jointBodyIndex;} + inline int& getBuoyancyBodyIndex() {return buoyancyBodyIndex;} + inline int& getContactBodyIndex() {return contactBodyIndex;} + + inline bool isFreeFallBody() const {return freeFallBodyIndex >= 0;} + inline bool isRealTimeBody() const {return realTimeBodyIndex >= 0;} + inline bool isJointBody() const {return jointBodyIndex >= 0; } + inline bool isBuoyancyBody() const {return buoyancyBodyIndex >= 0; } + inline bool isContactBody() const {return contactBodyIndex >= 0;} + inline bool isInKernel() const {return isFreeFallBody() || isRealTimeBody() || isJointBody() || isContactBody() || isBuoyancyBody();} + inline bool validateBodyLists() const {return (freeFallBodyIndex >= 0) + (realTimeBodyIndex >= 0) + + (jointBodyIndex >= 0) + (contactBodyIndex >= 0) <= 1;} + inline const int& getHumanoidConnectorCount() const {return numOfHumanoidConnectors;} + inline const int& getSecondPassConnectorCount() const {return numOfSecondPassConnectors;} + inline const int& getRealTimeConnectorCount() const {return numOfRealTimeConnectors;} + inline const int& getJointConnectorCount() const {return numOfJointConnectors;} + inline const int& getBuoyancyConnectorCount() const {return numOfBuoyancyConnectors;} + inline const int& getContactConnectorCount() const {return numOfContactConnectors;} + inline const int& getConnectorCount() const {return numOfConnectors;} + inline void incrementHumanoidConnectorCount() {++numOfHumanoidConnectors; ++numOfConnectors;} + inline void decrementHumanoidConnectorCount() {--numOfHumanoidConnectors; --numOfConnectors;} + inline void incrementSecondPassConnectorCount() {++numOfSecondPassConnectors; ++numOfConnectors;} + inline void decrementSecondPassConnectorCount() {--numOfSecondPassConnectors; --numOfConnectors;} + inline void incrementRealTimeConnectorCount() {++numOfRealTimeConnectors; ++numOfConnectors;} + inline void decrementRealTimeConnectorCount() {--numOfRealTimeConnectors; --numOfConnectors;} + inline void incrementJointConnetorCount() {++numOfJointConnectors; ++numOfConnectors;} + inline void decrementJointConnetorCount() {--numOfJointConnectors; --numOfConnectors;} + inline void incrementBuoyancyConnectorCount() {++numOfBuoyancyConnectors; ++numOfConnectors;} + inline void decrementBuoyancyConnectorCount() {--numOfBuoyancyConnectors; --numOfConnectors;} + inline void incrementContactConnectorCount() {++numOfContactConnectors; ++numOfConnectors;} + inline void decrementContactConnectorCount() {--numOfContactConnectors; -- numOfConnectors;} + }; + +} // namespace + diff --git a/App/v8tree/EnumProperty.h b/App/v8tree/EnumProperty.h new file mode 100644 index 0000000..a0c4e2e --- /dev/null +++ b/App/v8tree/EnumProperty.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include +#include + +#include "reflection/enumconverter.h" +#include "V8Tree/Property.h" + +namespace RBX { + +} \ No newline at end of file diff --git a/App/v8tree/Instance.h b/App/v8tree/Instance.h new file mode 100644 index 0000000..d18625a --- /dev/null +++ b/App/v8tree/Instance.h @@ -0,0 +1,778 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Reflection/Reflection.h" +#include "Reflection/Event.h" +#include "V8Tree/Property.h" +#include "V8Xml/Reference.h" +#include "V8Tree/Verb.h" + +#include "rbx/Countable.h" +#include "Util/Guid.h" + +#include +#include +#include "boost/weak_ptr.hpp" +#include "boost/shared_ptr.hpp" +#include "boost/enable_shared_from_this.hpp" +#include +#include + +namespace RBX { + + class Instance; + +// Convenience class +template < + class Class, + class BaseClass, + const char* const& sClassName, + Reflection::ClassDescriptor::Functionality functionality = Reflection::ClassDescriptor::PERSISTENT, + Security::Permissions security = Security::None + > +class DescribedCreatable : public Reflection::Described, functionality, security> +{ +protected: + inline DescribedCreatable() {} + template + inline DescribedCreatable(Arg0 arg0):Reflection::Described, functionality, security>(arg0) {} + template + inline DescribedCreatable(Arg0 arg0, Arg1 arg1):Reflection::Described, functionality, security>(arg0, arg1) {} +}; + +// Convenience class +template < + class Class, + class BaseClass, + const char* const& sClassName, + Reflection::ClassDescriptor::Functionality functionality = Reflection::ClassDescriptor::PERSISTENT, + Security::Permissions security = Security::None + > +class DescribedNonCreatable : public Reflection::Described, functionality, security> +{ +protected: + inline DescribedNonCreatable() {} + template + inline DescribedNonCreatable(Arg0 arg0):Reflection::Described, functionality, security>(arg0) {} + template + inline DescribedNonCreatable(Arg0 arg0, Arg1 arg1):Reflection::Described, functionality, security>(arg0, arg1) {} + template + inline DescribedNonCreatable(Arg0 arg0, Arg1 arg1, Arg2 arg2):Reflection::Described, functionality, security>(arg0, arg1, arg2) {} + template + inline DescribedNonCreatable(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3):Reflection::Described, functionality, security>(arg0, arg1, arg2, arg3) {} +}; + +class Instance; +class ServiceProvider; + + +/// A child has been added to the Notifier +struct ChildAdded { +public: + shared_ptr const child; + ChildAdded(Instance* child); + ChildAdded(const ChildAdded& event); +private: + ChildAdded& operator=(const ChildAdded&); +}; + +/// A child has been removed from the Notifier +struct ChildRemoved { +public: + shared_ptr const child; + ChildRemoved(Instance* child); + ChildRemoved(const ChildRemoved& event); +private: + ChildRemoved& operator=(const ChildAdded&); +}; + +/// A descendant has been added to the Notifier +struct DescendantAdded { + friend class Instance; +public: + shared_ptr const instance; + shared_ptr const parent; // The direct parent of the descendant +private: + DescendantAdded(shared_ptr instance, shared_ptr parent) + :instance(instance),parent(parent) + {} + DescendantAdded(Instance* instance, Instance* parent) + :instance(shared_from(instance)),parent(shared_from(parent)) + {} +}; + +/// A descendant of the Notifier is about to be removed +struct DescendantRemoving { +public: + shared_ptr const instance; + shared_ptr const parent; // The direct parent of the descendant before it is removed + DescendantRemoving(const shared_ptr& instance, const shared_ptr& parent) + :instance(instance),parent(parent) + {} +}; + +struct AncestorChanged { +public: + Instance* const child; + Instance* const oldParent; + Instance* const newParent; + AncestorChanged(Instance* child, Instance* oldParent, Instance* newParent) + :child(child),oldParent(oldParent),newParent(newParent) + {} +}; + +extern const char* const sInstance; + +typedef std::vector > Instances; + +class OnDemandInstance : public Allocator +{ +public: + rbx::signal)> childAddedSignal; + rbx::signal)> childRemovedSignal; + rbx::signal)> descendantAddedSignal; + rbx::signal)> descendantRemovingSignal; + rbx::signal)> instanceClonedSignal; + + struct ThreadWaitingForChild + { + std::string childName; + boost::function)> resumeFunction; + }; + std::vector threadsWaitingForChildren; + + virtual ~OnDemandInstance(){}; +}; + +class Instance + : public Reflection::Described + , public GuidItem + , public Diagnostics::Countable + , public boost::noncopyable +{ + friend class SetParentSentry; +private: + static void predelete(Instance* instance); + void predelete(); + friend class Creatable::Deleter; + + bool archivable; + bool isParentLocked; + bool robloxLocked; + bool isSettingParent; + + boost::flyweight name; + + copy_on_write_ptr children; + Instance* parent; // this field is set after initialization by Instance::addChild + +protected: + Instance(); + Instance(const char* name); + + // Destructor is protected. Call "destroy(instance)" instead of "delete instance". + virtual ~Instance(); + + boost::scoped_ptr onDemandPtr; + + virtual OnDemandInstance* initOnDemand(); + +public: + const OnDemandInstance* onDemandRead() const; + OnDemandInstance* onDemandWrite(); + + virtual void onGuidChanged(); + + void lockParent() { isParentLocked = true; } + void unlockParent() { isParentLocked = false; } + bool getIsParentLocked() const { return isParentLocked; } + void securityCheck() const; + void securityCheck(RBX::Security::Context& context) const; + + static Reflection::PropDescriptor propArchivable; + bool getIsArchivable() const { return archivable; } + virtual void setIsArchivable(bool value); + + // Call "destroy" to delete an Instance fully so it can't be used again + virtual void destroy(); + void remove(); + + enum SaveFilter + { + SAVE_WORLD = 0, + SAVE_GAME = 1, + SAVE_ALL = 2, + }; + + static const Reflection::PropDescriptor desc_Name; + static const Reflection::RefPropDescriptor propParent; + static const Reflection::PropDescriptor propRobloxLocked; + + enum CombinedSignalType + { + CHILD_ADDED, + CHILD_REMOVED, + PROPERTY_CHANGED, + EVENT_INVOCATION, + OUTFIT_CHANGED, + ANCESTRY_CHANGED, + CLUMP_CHANGED, + SLEEPING_CHANGED, + HUMANOID_CHANGED + }; + + class ICombinedSignalData + { + public: + virtual ~ICombinedSignalData() {}; + }; + + class ChildAddedSignalData : public ICombinedSignalData + { + public: + ChildAddedSignalData(const shared_ptr& child) + : child(child) + {} + + shared_ptr child; + }; + class ChildRemovedSignalData : public ICombinedSignalData + { + public: + ChildRemovedSignalData(const shared_ptr& child) + : child(child) + {} + + shared_ptr child; + }; + class AncestryChangedSignalData: public ICombinedSignalData + { + public: + AncestryChangedSignalData(const shared_ptr& child, const shared_ptr& newParent) + : child(child) + , newParent(newParent) + {} + + shared_ptr child; + shared_ptr newParent; + }; + class OutfitChangedSignalData: public ICombinedSignalData + { + public: + OutfitChangedSignalData() + {} + }; + + class PropertyChangedSignalData : public ICombinedSignalData + { + public: + PropertyChangedSignalData(const Reflection::PropertyDescriptor* propertyDescriptor) + :propertyDescriptor(propertyDescriptor) + {} + const Reflection::PropertyDescriptor* propertyDescriptor; + }; + + class EventInvocationSignalData : public ICombinedSignalData + { + public: + EventInvocationSignalData(const Reflection::EventDescriptor* eventDescriptor, const Reflection::EventArguments* eventArguments, const SystemAddress* target) + :eventDescriptor(eventDescriptor) + ,eventArguments(eventArguments) + ,target(target) + {} + const Reflection::EventDescriptor* eventDescriptor; + const Reflection::EventArguments* eventArguments; + const SystemAddress* target; + }; + + class HumanoidChangedSignalData: public ICombinedSignalData + { + public: + HumanoidChangedSignalData() + {} + }; + + virtual void humanoidChanged(); + + + void childAddedSignal(shared_ptr& inst) { if(onDemandRead()) onDemandWrite()->childAddedSignal(inst); } + rbx::signal)>* getOrCreateChildAddedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->childAddedSignal : NULL; }; + + void childRemovedSignal(shared_ptr& inst) { if(onDemandRead()) onDemandWrite()->childRemovedSignal(inst); } + rbx::signal)>* getOrCreateChildRemovedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->childRemovedSignal : NULL; }; + + void descendantAddedSignal(shared_ptr& inst) { if(onDemandRead()) onDemandWrite()->descendantAddedSignal(inst); } + rbx::signal)>* getOrCreateDescendantAddedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->descendantAddedSignal : NULL; }; + + void descendantRemovingSignal(const shared_ptr& inst) { if(onDemandRead()) onDemandWrite()->descendantRemovingSignal(inst); } + rbx::signal)>* getOrCreateDescendantRemovingSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->descendantRemovingSignal : NULL; }; + + rbx::signal, shared_ptr)> ancestryChangedSignal; + rbx::signal propertyChangedSignal; + + // combinedSignal is an optimization. You could use the regular signals, like childAddedSignal. + // However, if you are listening to many signals then you can save memory by listening to just the combinedSignal + rbx::signal combinedSignal; + + shared_ptr clone(CreatorRole creatorRole); + virtual shared_ptr luaClone(); //Just like regular clone, but it enforces the Instance limits + static XmlElement* toNewXmlRoot(Instance* instance, RBX::CreatorRole creatorRole); // DB 12/5/05 - added here to centralize the spawner behavior - to, from XML + + void removeAllChildren(); + + // security: this call has checks that prevent it from being called from most code. + void destroyAllChildrenLua(); + + const std::string& getClassNameStr() const { return getClassName().toString(); } // used by reflection + + std::string getReadableDebugId(int scopeLength) const { return getGuid().readableString(scopeLength); } + std::string getReadableDebugId() const { return getGuid().readableString(); } + std::string getReadableDebugId(int scopeLength) { return getGuid().readableString(scopeLength); } + + inline Instance* getParent() {return parent;} + inline const Instance* getParent() const {return parent;} +private: + inline Instance* getParentDangerous() const {return parent;} // only used by refProp descriptor + +public: + inline Instance* getRootAncestor() {return parent ? parent->getRootAncestor() : this;} + inline const Instance* getRootAncestor() const {return parent ? parent->getRootAncestor() : this;} + + static Instance* getRootAncestor(Instance* instance) { return instance ? instance->getRootAncestor() : NULL; } + static const Instance* getRootAncestor(const Instance* instance) { return instance ? instance->getRootAncestor() : NULL; } + + bool getRobloxLocked() const { return robloxLocked; } + void setRobloxLocked(bool value); + + bool contains(const Instance* child) const; // Recursive. Also returns true if this==child + + void setParent(Instance* instance) { setParentInternal(instance, false); } + bool setLockedParent(Instance* instance) { return setParentInternal(instance, true); } + void setParent2(shared_ptr instance) { + setParent(instance.get()); + } + void promoteChildren(); + static const void* getSetParentAddr(); + + // locks the parent and then sets it + // if this fails to set the parent it will unlock the parent before returning + // if the parent is successfully set the parent will stay locked + void setAndLockParent(Instance* instance); + + const std::string& getName() const {return name.get(); } + virtual void setName(const std::string& value); + + std::string getFullName() const; // Render up to (but not including) the root node + std::string getFullNameForReflection() { return getFullName(); } + + bool isAncestorOf(const Instance* descendant) const + { + if (descendant==NULL) + return false; + else if (descendant->getParent()==this) + return true; + else + return isAncestorOf(descendant->getParent()); + } + + bool isAncestorOf2(shared_ptr descendant) { return isAncestorOf(descendant.get()); } + bool isDescendantOf2(shared_ptr ancestor) { return isDescendantOf(ancestor.get()); } + + // isDescendantOf will return true if parent==NULL (consistent with the fact that a top-level Instance has parent==NULL) + bool isDescendantOf(const Instance* ancestor) const + { + const Instance* parent = getParent(); + if (ancestor==parent) + return true; + else if (parent!=NULL) + return parent->isDescendantOf(ancestor); + else + return false; + } + + template + Type* findFirstAncestorOfType() + { + Instance* parent = getParent(); + if (Type* parentType = this->fastDynamicCast(parent)) + return parentType; + else if (parent!=NULL) + return parent->findFirstAncestorOfType(); + else + return NULL; + } + + size_t numChildren() const {return children ? children->size() : 0;} + + int findChildIndex(const Instance* instance) const; + virtual int getPersistentDataCost() const ; + static int computeStringCost(const std::string& value) + { + return std::max(1, value.length() / 100); + } + + + Instance* getChild(size_t index) {return (*children)[index].get();} + const Instance* getChild(size_t index) const {return (*children)[index].get();} + + void waitForChild(std::string childName, boost::function)> resumeFunction, boost::function errorFunction); + void checkParentWaitingForChildren(); + + // Find the first ancestor of a given descendant + shared_ptr findFirstAncestorOf(const Instance* descendant) const; + + // TODO: findFirstChildByName should return const Instance* + + const Instance* findConstFirstChildByName(const std::string& findName) const; + Instance* findFirstChildByName(const std::string& findName) { + return const_cast(findConstFirstChildByName(findName)); + } + Instance* findFirstChildByNameDangerous(const std::string& findName) const { // used by reflection - breaking from const to non const + return const_cast(findConstFirstChildByName(findName)); + } + + // breadth-first search + Instance* findFirstChildByNameRecursive(const std::string& findName); + + // Used for Reflection: + shared_ptr findFirstChildByName2(std::string findName, bool recursive) { + return recursive ? shared_from(findFirstChildByNameRecursive(findName)) : shared_from(findFirstChildByName(findName)); + } + + // queryTypedChild + template + Type* queryTypedChild(int index) { + return dynamic_cast((*children)[index].get()); + } + template + const Type* queryTypedChild(int index) const { + return dynamic_cast((*children)[index].get()); + } + + // getTypedChild + template + Type* getTypedChild(int index) { + return rbx_static_cast((*children)[index].get()); + } + + template + const Type* getTypedChild(int index) const { + return rbx_static_cast((*children)[index].get()); + } + + // queryTypedParent + template + Type* queryTypedParent() { + return dynamic_cast(parent); + } + + template + const Type* queryTypedParent() const { + return dynamic_cast(parent); + } + + // getTypedParent + template + Type* getTypedParent() { + return rbx_static_cast(parent); + } + + template + const Type* getTypedParent() const { + return rbx_static_cast(parent); + } + + // getTypedRoot + template + Type* getTypedRoot() { + RBXASSERT(dynamic_cast(this)); + if (Type* typedParent = dynamic_cast(parent)) { + return typedParent->template getTypedRoot(); + } + else { + return static_cast(this); + } + } + template + const Type* getTypedRoot() const { + RBXASSERT(dynamic_cast(this)); + if (const Type* typedParent = dynamic_cast(parent)) { + return typedParent->template getTypedRoot(); + } + else { + return static_cast(this); + } + } + + const copy_on_write_ptr& getChildren() const { return children; } + + // Used for reflection. Note that it might return NULL (or an empty container) + // TODO - this is dangerous? getting const children? + shared_ptr getChildren2() { return children.read(); } + + template + inline void visitChildren(const Func& func) const { + if (children) + { + boost::shared_ptr c(children.read()); + Instances::const_iterator end = c->end(); + for (Instances::const_iterator iter = c->begin(); iter!=end; ++iter) + { + const_cast(func)(*iter); + } + } + } + + template + inline int countDescendantsOfType() const { + int total = fastDynamicCast() ? 1 : 0; + if (children) + { + boost::shared_ptr c(children.read()); + Instances::const_iterator end = c->end(); + for (Instances::const_iterator iter = c->begin(); iter!=end; ++iter) + { + total += (*iter)->countDescendantsOfType(); + } + } + return total; + } + + template + inline void visitDescendants(const Func& func) const { + if (children) + { + boost::shared_ptr c(children.read()); + Instances::const_iterator end = c->end(); + for (Instances::const_iterator iter = c->begin(); iter!=end; ++iter) + { + const_cast(func)(*iter); + (*iter)->visitDescendants(func); + } + } + } + + template + const C* findConstFirstChildOfType() const + { + if (children) + { + Instances::const_iterator end = children->end(); + for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) { + const C* c = fastDynamicCast(iter->get()); + if (c!=NULL) + return c; + } + } + return NULL; + } + + template + C* findFirstChildOfType() + { + return const_cast(findConstFirstChildOfType()); + } + + Instance* findFirstChildOfType(const std::string& className) + { + if (children) + { + Instances::const_iterator end = children->end(); + for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) { + if(iter->get()->getClassNameStr() == className) + return iter->get(); + } + } + return NULL; + } + + template + C* findFirstDescendantOfType() + { + if (children) + { + Instances::const_iterator end = children->end(); + for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) { + Instance* i = iter->get(); + if (C* foundChild = fastDynamicCast(i)) { + return foundChild; + } + if (C* foundDesc = i->findFirstDescendantOfType()) { + return foundDesc; + } + } + } + return NULL; + } + + template + const C* findConstFirstDescendantOfType() const + { + if (children) + { + Instances::const_iterator end = children->end(); + for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) { + const Instance* i = iter->get(); + if (const C* foundChild = fastDynamicCast(i)) { + return foundChild; + } + if (const C* foundDesc = i->findConstFirstDescendantOfType()) { + return foundDesc; + } + } + } + return NULL; + } + + template + void destroyDescendantsOfType() + { + if (children) + { + Instances::const_iterator end = children->end(); + for (Instances::const_iterator iter = children->begin(); iter!=end; ++iter) { + if (const C* c = fastDynamicCast(iter->get())) + { + iter->get()->destroy(); + } else { + iter->get()->destroyDescendantsOfType(); + } + } + } + } + + static Instance* findCommonNode(Instance* i1, Instance* i2) + { + if (i1 == i2) + return i1; + + if (!i1) + return NULL; + if (i1->isAncestorOf(i2)) + return i1; + + if (!i2) + return NULL; + if (i2->isAncestorOf(i1)) + return i2; + + return findCommonNode(i1->getParent(), i2->getParent()); + } + + // Override with class-specific rules about what can be a child or parent + bool canAddChild(const Instance* instance, bool checkParent = true) const { + if (instance->contains(this)) + return false; // No circular ownership, please! + if (checkParent && instance->getParent() == this) + return false; // Already a child! + if (this->askForbidChild(instance)) + return false; + if (instance->askForbidParent(this)) + return false; + if (this->askAddChild(instance)) + return true; + if (instance->askSetParent(this)) + return true; + return false; + } + bool canAddChild(const shared_ptr& instance) const { + return (instance!=NULL) && canAddChild(instance.get()); + } + bool canSetParent(const Instance* instance) const { + return (instance==NULL) || instance->canAddChild(this); + } + + template + bool canSetChildren(Iter first, Iter last) const + { + for (; first != last; ++first) + { + if (!canAddChild(*first)) + return false; + } + return true; + } + + template + void setChildren(Iter first, Iter last) + { + for (; first != last; ++first) + { + Instance* instance = *first; + RBXASSERT(canAddChild(instance)); + instance->setParent(this); + } + } + + virtual bool canClientCreate() { return false; } + // This convenience function gets called when this Instances is added to or removed from the DataModel. + // You can use it to find Services, connect to and disconnect from Notifiers, etc. + virtual void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider) {} + + void readProperties(const XmlElement* container, IReferenceBinder& binder); + + virtual shared_ptr createChild(const RBX::Name& className, RBX::CreatorRole creatorRole); + void read(const XmlElement* element, IReferenceBinder& binder, RBX::CreatorRole creatorRole); + void readChildren(const XmlElement* element, IReferenceBinder& binder, RBX::CreatorRole creatorRole); + void readChild(const XmlElement* childElement, IReferenceBinder& binder, RBX::CreatorRole creatorRole); + + virtual XmlElement* writeXml(const boost::function& isInScope, RBX::CreatorRole creatorRole); + + void writeChildren(XmlElement* container, const boost::function& isInScope, RBX::CreatorRole creatorRole, const SaveFilter saveFilter = SAVE_ALL); + void writeChildren(XmlElement* container, RBX::CreatorRole creatorRole, const SaveFilter saveFilter = SAVE_ALL); + + void raisePropertyChanged(const RBX::Reflection::PropertyDescriptor& descriptor); + void raiseEventInvocation(const RBX::Reflection::EventDescriptor& descriptor, const RBX::Reflection::EventArguments& args, const SystemAddress* target); + + ////////////////////////////////////////////////////////////////////////////////////////// + +protected: + //Actively prevent the adding + virtual void verifySetParent(const Instance* newParent) const {}; + virtual void verifySetAncestor(const Instance* const newParent, const Instance* const instanceGettingNewParent) const; + //Actively prevent the adding (parent side) + virtual void verifyAddChild(const Instance* newChild) const {}; + virtual void verifyAddDescendant(const Instance* newParent, const Instance* instanceGettingNewParent) const; + + // Don't call this directly. Call canAddChild + virtual bool askAddChild(const Instance* instance) const; + // Don't call this directly. Call canAddChild + virtual bool askForbidChild(const Instance* instance) const; + + // Don't call this directly. Call canSetParent + virtual bool askForbidParent(const Instance* instance) const; + // Don't call this directly. Call canSetParent + virtual bool askSetParent(const Instance* instance) const; + + + virtual void onAncestorChanged(const AncestorChanged& event); + + virtual void onDescendantAdded(Instance* instance); + virtual void onDescendantRemoving(const shared_ptr& instance); + + virtual void onChildAdded(Instance* child) {} + virtual void onChildRemoving(Instance* child) {} + virtual void onChildRemoved(Instance* child) {} + virtual void onChildChanged(Instance* instance, const PropertyChanged& event); + + virtual void onPropertyChanged(const Reflection::PropertyDescriptor& descriptor) {} + + + + virtual void readProperty(const XmlElement* propertyElement, IReferenceBinder& binder); + + void raiseChanged(const RBX::Reflection::PropertyDescriptor& descriptor) + { + raisePropertyChanged(descriptor); + } + +private: + static void signalDescendantAdded(Instance* instance, Instance* beginParent, Instance* endParent); + static void signalDescendantRemoving(const shared_ptr& instance, Instance* beginParent, Instance* endParent); + + void writeProperties(XmlElement* container) const; + bool setParentInternal(Instance* instance, bool ignoreLock); +}; + +} // namespace RBX diff --git a/App/v8tree/Property.h b/App/v8tree/Property.h new file mode 100644 index 0000000..0aab86a --- /dev/null +++ b/App/v8tree/Property.h @@ -0,0 +1,46 @@ +#pragma once + +#include "Reflection/Property.h" +#include "V8Xml/Reference.h" +#include "V8Xml/XmlElement.h" +#include "Util/Object.h" + +#include "G3D/Vector3.h" +#include "G3D/Color3.h" +#include +#include +#include + +namespace RBX { + + class PropertyChanged + { + RBX::Reflection::Property property; + public: + const RBX::Reflection::Property& getProperty() const { return property; } + const RBX::Reflection::PropertyDescriptor& getDescriptor() const { return property.getDescriptor(); } + const RBX::Name& getName() { return property.getName(); } + + PropertyChanged(const PropertyChanged& other) + : property(other.property) + {} + + private: + friend class Instance; + PropertyChanged(const RBX::Reflection::Property& p) + : property(p) + {} + }; + +// TODO: Move this out of Reflection? +// Some standard categories +#define category_Data "Data" +#define category_Behavior "Behavior" +#define category_State "State" +#define category_Appearance "Appearance" +#define category_Team "Team" +#define category_Image "Image" +#define category_Video "Video" +#define category_Control "Control" + +} diff --git a/App/v8tree/Service.h b/App/v8tree/Service.h new file mode 100644 index 0000000..406c0e1 --- /dev/null +++ b/App/v8tree/Service.h @@ -0,0 +1,254 @@ +#pragma once + +#include "V8Tree/Instance.h" +#include +#include + +namespace RBX +{ + // A Service is an instance that is "Singleton" in the scope of its containing + // ServiceProvider. In other words, a Service-derived class is unique within the + // child tree of a ServiceProvider. + class Service + { + public: + const bool isPublic; + protected: + Service(bool isPublic=true) + :isPublic(isPublic) + { + } + ~Service() + { + } + }; + + // Design decision: ServiceProvider liberally uses mutable members and declares const + // member functions that change data. The idea here is that a ServiceProvider doesn't really + // change fundamentally when creating and providing Services. It lets clients get a service + // when const + // TODO: consider the relative merits of returning Services as shared_ptr: safer, but more likely to lead to ptr leaks? + extern const char* const sServiceProvider; + class ServiceProvider + : public DescribedNonCreatable + { + private: + typedef DescribedNonCreatable Super; + static Reflection::BoundFuncDesc(std::string)> func_FindService; + static Reflection::BoundFuncDesc(std::string)> func_GetService; + static Reflection::BoundFuncDesc(std::string)> dep_service; + static Reflection::BoundFuncDesc(std::string)> dep_GetService; + typedef std::vector< shared_ptr > ServiceArray; + mutable ServiceArray serviceArray; + mutable std::map > serviceMap; + public: + rbx::signal closingSignal; + rbx::signal closingLateSignal; + rbx::signal service)> serviceAddedSignal; + rbx::signal service)> serviceRemovingSignal; + + ServiceProvider(); + ServiceProvider(const char* name); + + template + ServiceClass* find() const + { + BOOST_STATIC_ASSERT((boost::is_base_of::value)); + BOOST_STATIC_ASSERT((boost::is_base_of::value)); + + size_t index = getClassIndex(); + + ServiceClass* service; + if (index+1>serviceArray.size()) + { + // serviceArray has an empty entry for this service now + serviceArray.resize(index+1, shared_ptr()); + service = NULL; + } + else + { + service = static_cast(serviceArray[index].get()); + if (service!=NULL) + return service; + } + + if (ServiceClass::isNullClassName()) + return NULL; + + // See if the service is in the className map + shared_ptr i = findServiceByClassName(ServiceClass::className()); + if (!i) + return NULL; + service = boost::polymorphic_downcast(i.get()); + serviceArray[index] = i; + return service; + } + + //mutable RBX::reentrant_concurrency_catcher threadGuard; + template + ServiceClass* create() const + { + //RBX::reentrant_concurrency_catcher::scoped_lock lock(threadGuard); + + ServiceClass* service = this->find(); + if (service==NULL) + { + // If all else fails, create the service and put it in the table and map + shared_ptr s = Creatable::create(); + + service = s.get(); + + // setParent can throw, so do not mutate any internal state until + // setParent has returned without throwing. + service->setAndLockParent(const_cast(this)); + size_t index = getClassIndex(); + serviceArray[index] = s; + + // By now onDescendantAdded will have been called, which will add the service to serviceMap: + RBXASSERT((ServiceClass::className()==RBX::Name::getNullName()) || serviceMap.find(&ServiceClass::className())!=serviceMap.end()); + + } + return service; + } + + static const ServiceProvider* findServiceProvider(const Instance* context) + { + if (const Instance* root = Instance::getRootAncestor(context)) { + if (const ServiceProvider* serviceProvider = Instance::fastDynamicCast(root)) { + return serviceProvider; + } + } + return NULL; + } + + template + static ServiceClass* find(const Instance* context) + { + if (const ServiceProvider* serviceProvider = findServiceProvider(context)) + return serviceProvider->find(); + return NULL; + } + + template + static ServiceClass* find(const ServiceProvider* serviceProvider) + { + if (serviceProvider!=NULL) + return serviceProvider->find(); + return NULL; + } + + template + static ServiceClass* create(const Instance* context) + { + if (const ServiceProvider* serviceProvider = findServiceProvider(context)) + return serviceProvider->create(); + return NULL; + } + + template + static ServiceClass* create(const ServiceProvider* serviceProvider) + { + if (serviceProvider!=NULL) + return serviceProvider->create(); + return NULL; + } + + // Less efficient factory functions that use classNames. Use only if + // you don't know the type of Service you want. + static shared_ptr create(Instance* context, const RBX::Name& name); + shared_ptr getPublicServiceByClassNameString(std::string name); + + protected: + /*override*/ shared_ptr createChild(const RBX::Name& className, RBX::CreatorRole creatorRole); + /*override*/ void onDescendantRemoving(const shared_ptr& instance); + /*override*/ void onDescendantAdded(Instance* instance); + /*override*/ void onChildAdded(Instance* child); + /*override*/ void onChildRemoving(Instance* child); + + /* override */ bool askAddChild(const Instance* instance) const + { + return dynamic_cast(instance) != NULL; + } + + void clearServices(); + + private: + // Returns a new number each time it is called (starting at 0) + static size_t newIndex(); + + template + static size_t doGetClassIndex() + { + static size_t index = newIndex(); + return index; + } + + template + static void callDoGetClassIndex() + { + doGetClassIndex(); + } + + shared_ptr findServiceByClassName(const RBX::Name& className) const; + shared_ptr findPublicServiceByClassNameString(std::string name); + + private: + template + static size_t getClassIndex() + { + static boost::once_flag flag = BOOST_ONCE_INIT; + boost::call_once(&callDoGetClassIndex, flag); + return doGetClassIndex(); + } + + }; + + + // A convenience class. Given an Instance* context, it will find the Service. You can treat it + // like a Service*. Please note that it might be NULL if the context isn't a child of a ServiceProvider (like DataModel) + template + class ServiceClient + { + Instance* context; + mutable shared_ptr service; + public: + ServiceClient(Instance* context) + :context(context) + { + } + bool isNull() const + { + return findService()==NULL; + } + operator S*() + { + return createService(); + } + operator const S*() const + { + return createService(); + } + const S* operator->() const + { + return createService(); + } + S* operator->() + { + return createService(); + } + private: + S* findService() const + { + if (!service) + service = shared_from(ServiceProvider::find(context)); + return service.get(); + } + S* createService() const + { + if (!service) + service = shared_from(ServiceProvider::create(context)); + return service.get(); + } + }; + +} diff --git a/App/v8tree/Verb.h b/App/v8tree/Verb.h new file mode 100644 index 0000000..07993c5 --- /dev/null +++ b/App/v8tree/Verb.h @@ -0,0 +1,164 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include +#include + +#include "rbx/Debug.h" +#include "security/FuzzyTokens.h" +#include "v8datamodel/HackDefines.h" + +LOGGROUP(Verbs) + +namespace RBX { + +class Name; +class VerbContainer; + +class RBXInterface IDataState +{ +public: + // Sets the document's "dirty" bit + virtual void setDirty(bool dirty) = 0; + virtual bool isDirty() const = 0; +}; + +class RBXBaseClass Verb +{ + const Name& name; +protected: + VerbContainer* const container; + Verb(VerbContainer* container, const std::string& name, bool blacklisted = false); + Verb(VerbContainer* container, const Name& name, bool blacklisted = false); + bool verbSecurity; +public: + virtual ~Verb(); + virtual bool isEnabled() const { return true; } + virtual bool isChecked() const { return false; } + virtual bool isSelected() const { return false; } + + virtual std::string getText() const { return std::string(); } + const Name& getName() const {return name;} + + virtual void doIt(IDataState* dataState) = 0; + + VerbContainer* getContainer() const { return container; } + + bool getVerbSecurity() const { return verbSecurity; } + + static inline void doItWithChecks(Verb* const verb, IDataState* dataState) + { +#if !defined(RBX_STUDIO_BUILD) + if (verb->getVerbSecurity()) + { + RBX::Security::setHackFlagVs(RBX::Security::hackFlag9, HATE_VERB_SNATCH); + } + else +#endif + { + verb->doIt(dataState); + } + } +}; + +// This version of Verb lets you pass in callbacks for the implementation +class BoundVerb : public Verb +{ + boost::function doItFunction; + boost::function isEnabledFunction; + boost::function isCheckedFunction; + boost::function getTextFunction; + +public: + BoundVerb(VerbContainer* container, const char* name, + boost::function doItFunction, + boost::function isEnabledFunction = NULL, + boost::function isCheckedFunction = NULL, + boost::function getTextFunction = NULL) + : Verb(container, name) + , doItFunction(doItFunction) + , isEnabledFunction(isEnabledFunction) + , isCheckedFunction(isCheckedFunction) + , getTextFunction(getTextFunction) + { + } + + virtual bool isEnabled() const + { + if (!isEnabledFunction) + return Verb::isEnabled(); + return isEnabledFunction(container); + } + + virtual bool isChecked() const + { + if (!isCheckedFunction) + return Verb::isChecked(); + return isCheckedFunction(container); + } + + virtual std::string getText() const + { + if (!getTextFunction) + return Verb::getText(); + return getTextFunction(container); + } + + virtual void doIt(IDataState* dataState) + { + if (doItFunction) + doItFunction(container); + } + +}; + +class NullVerb : public Verb +{ +public: + NullVerb(VerbContainer* container, const std::string& name) + : Verb(container, name) + { + } + + /*override*/ bool isEnabled() const { return false; } + /*override*/ void doIt(IDataState* dataState) {} +}; + +class VerbContainer +{ + friend class Verb; + typedef std::map Verbs; + Verbs whitelistVerbs; // used for UI verbs + Verbs blacklistVerbs; // used for tools + VerbContainer* parent; + + void addWhitelistVerb(Verb* verb); + void addBlacklistVerb(Verb* verb); + void removeVerb(Verb* verb); + +public: + VerbContainer(VerbContainer* parent); + virtual ~VerbContainer(); + Verb* getVerb(const Name& name); + Verb* getVerb(const std::string& name); + Verb* getWhitelistVerb(const Name& name); + Verb* getWhitelistVerb(const std::string& name); + // This version of getVerb is used as a minor obsfucation of a string. + Verb* getWhitelistVerb(const std::string& prefix, const std::string& name, const std::string& suffix); + void setVerbParent(VerbContainer* parent); + VerbContainer* getVerbParent() const { return parent; } + + template + void eachVerbName(F f, bool includeParent = true) + { + for (Verbs::const_iterator iter = whitelistVerbs.begin(); iter != whitelistVerbs.end(); ++iter) + f(iter->first); + for (Verbs::const_iterator iter = blacklistVerbs.begin(); iter != blacklistVerbs.end(); ++iter) + f(iter->first); + if (includeParent && parent) + parent->eachVerbName(f); + } +}; + +} // namespace RBX diff --git a/App/v8world/Assembly.h b/App/v8world/Assembly.h new file mode 100644 index 0000000..dfcbcc0 --- /dev/null +++ b/App/v8world/Assembly.h @@ -0,0 +1,220 @@ +/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/IndexedMesh.h" +#include "V8World/Enum.h" +#include "V8World/Primitive.h" +#include "V8World/IPipelined.h" +#include "Util/ComputeProp.h" +#include "Util/PhysicsCoord.h" +#include "Util/Average.h" +#include "rbx/Debug.h" +#include +#include "boost/intrusive/list.hpp" +#include "Network/CompactCFrame.h" + +namespace RBX { + + class Joint; + class Primitive; + class Clump; + class Edge; + class MotorJoint; + class Clump; + class SimulateStage; + class AssemblyHistory; + + typedef boost::intrusive::list_base_hook< boost::intrusive::tag > SimulateStageHook; + + class Assembly + : public IPipelined + , public boost::noncopyable + , public IndexedMesh + , public SimulateStageHook + { + friend class SimJobStage; + + public: + typedef enum {Sim_SendIfSim, Sim_BufferZone, NoSim_Send, NoSim_SendIfSim, NoSim_Send_Anim, NoSim_SendIfSim_Anim, NUM_PHASES, Fixed, NOT_ASSIGNED} FilterPhase; + + private: + bool animationControlled; + mutable bool inCode; + + unsigned char networkHumanoidState; + + AssemblyHistory* history; + + Sim::AssemblyState state; + + G3D::Array assemblyExternalEdges; + G3D::Array assemblyMotors; + + // SimJobStage - index into either the stage, or a mechanism + class SimJob* simJob; + + // SleepStage - helper for recursive algorithm - elminate need for external std::set + int recursivePassId; + int recursiveDepth; + + // SpatialFilter - helper for current Phase + FilterPhase filterPhase; + + // Compute Props + ComputeProp maxRadius; // farthest point on the primitives - used for getting max velocity + float computeAssemblyMaxRadius(); + + void gatherPrimitiveExternalEdges(Primitive* p); + + Clump* getAssemblyClump(); + const Clump* getConstAssemblyClump() const; + + void getAssemblyMotors(G3D::Array& motors, bool nonAnimatedOnly); + void getConstAssemblyMotors(G3D::Array& motors, bool nonAnimatedOnly) const; + + ///////////////////////////////////////////////////////////// + // IndexedMesh + // + /*override*/ void onLowersChanged(); // Primitives added/removed beneath me + + public: + Assembly(); + + ~Assembly(); + + Primitive* getAssemblyPrimitive(); + const Primitive* getConstAssemblyPrimitive() const; + + static Assembly* getPrimitiveAssembly(Primitive* p); + static const Assembly* getConstPrimitiveAssembly(const Primitive* p); + static Assembly* getPrimitiveAssemblyFast(Primitive* p); // primitive must have an assembly + + static bool isAssemblyRootPrimitive(const Primitive* p); + + Assembly* otherAssembly(Edge* edge); + const Assembly* otherConstAssembly(const Edge* edge) const; + + ///////////////////////////////////////////////////////////////// + + bool getCanThrottle() const; + + static bool computeCanThrottle(Edge* edge); + + Vector2 get2dPosition() const; + + static bool computeIsGroundingPrimitive(const Primitive* p); // in the engine, requestFIxed or RigidJoined to a fixed primitive + + bool computeIsGrounded() const; + + void notifyMovedFromInternalPhysics(); + + void notifyMovedFromExternal(); + + // From SpatialFilter + FilterPhase getFilterPhase() const {return filterPhase;} + void setFilterPhase(FilterPhase value) {filterPhase = value;} + + // From SimJobStage + void setSimJob(SimJob* s) {simJob = s;} + SimJob* getSimJob() {return simJob;} + const SimJob* getConstSimJob() const {return simJob;} + + // From SleepStage + void reset(Sim::AssemblyState newState); // resets state, sleep count, running average + bool sampleAndNotMoving(); + bool preventNeighborSleep(); + void wakeUp(); // moving from sleeping to non-Sleeping state + + Sim::AssemblyState getAssemblyState() const; + void setAssemblyState(Sim::AssemblyState value) {state = value;} + + void setRecursivePassId(int value) {recursivePassId = value;} + int getRecursivePassId() const {return recursivePassId;} + + void setRecursiveDepth(int value) {recursiveDepth= value;} + int getRecursiveDepth() const {return recursiveDepth;} + + bool getAssemblyIsMovingState() const { + return (Sim::isMovingAssemblyState(state)); + } + + float computeMaxRadius() {return maxRadius.getValue();} + float getLastComputedRadius() const {return maxRadius.getLastComputedValue();} + float isComputedRadiusDirty() const {return maxRadius.getDirty();} + + // Replicated attributes (essentially used as Humanoid State) + unsigned char getNetworkHumanoidState() const {return networkHumanoidState;} + void setNetworkHumanoidState(unsigned char value) {networkHumanoidState = value;} + + const G3D::Array& getAssemblyEdges(); + + void setPhysics(const G3D::Array& motorAngles, const PV& pv); + void getPhysics(G3D::Array& motorAngles) const; + + template + inline void visitAssemblies(Func func) { + this->visitMeAndChildren(func); + } + + template + inline void visitDescendentAssemblies(Func func) { + this->visitDescendents(func); + } + + template + inline void visitConstDescendentAssemblies(Func func) const { + this->visitConstDescendents(func); + } + + bool isAnimationControlled() const { return animationControlled; } + void setAnimationControlled(bool val) { animationControlled = val; } + + // Primitive Visiting Functions + + private: + template + inline void visitPrimitivesImpl(Func func, Primitive* p) { + func(p); + for (int i = 0; i < p->numChildren(); ++i) { + Primitive* child = p->getTypedChild(i); + if (!Assembly::isAssemblyRootPrimitive(child)) { + visitPrimitivesImpl(func, child); + } + } + } + + + template + inline Primitive* findFirstPrimitiveImpl(Func func, Primitive* p) { + if (func(p)) { + return p; + } + for (int i = 0; i < p->numChildren(); ++i) { + Primitive* child = p->getTypedChild(i); + if (!Assembly::isAssemblyRootPrimitive(child)) { + if (findFirstPrimitiveImpl(func, child)) { + return child; + } + } + } + return NULL; + } + + public: + template + inline void visitPrimitives(Func func) { + Primitive* p = getAssemblyPrimitive(); + RBXASSERT(p); + visitPrimitivesImpl(func, p); + } + + template + inline Primitive* findFirstPrimitive(Func func) { + Primitive* p = getAssemblyPrimitive(); + RBXASSERT(p); + return findFirstPrimitiveImpl(func, p); + } + }; + +}// namespace diff --git a/App/v8world/AssemblyHistory.h b/App/v8world/AssemblyHistory.h new file mode 100644 index 0000000..a4690b7 --- /dev/null +++ b/App/v8world/AssemblyHistory.h @@ -0,0 +1,44 @@ +/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/PhysicsCoord.h" +#include "Util/Average.h" +#include "rbx/Debug.h" + +namespace RBX { + + class Assembly; + + class AssemblyHistory + { + private: + Average average; + int stepsSinceSample; + int awakeSteps; + float maxDeviationSquared; + + static size_t sampleSkip(); + static size_t bufferSize(); + static float sleepTolerance(); + static float sleepToleranceSquared(); + + bool notMoving(); + void updateMaxDeviationSquared(); + PhysicsCoord getAssemblyPhysicsCoord(Assembly& a); + + public: + AssemblyHistory(Assembly& a); + + ~AssemblyHistory(); + + void clear(Assembly& a); + + bool sampleAndNotMoving(Assembly& a); + + bool preventNeighborSleep(); + + void wakeUp(); + }; + +}// namespace diff --git a/App/v8world/AssemblyStage.h b/App/v8world/AssemblyStage.h new file mode 100644 index 0000000..06d8c45 --- /dev/null +++ b/App/v8world/AssemblyStage.h @@ -0,0 +1,37 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/EdgeBuffer.h" + +namespace RBX { + class Assembly; + class Primitive; + + class AssemblyStage : public EdgeBuffer { + public: + AssemblyStage(IStage* upstream, World* world); + + ~AssemblyStage(); + + /*override*/ IStage::StageType getStageType() const {return IStage::ASSEMBLY_STAGE;} + + void onFixedAssemblyRootAdded(Assembly* a); + void onFixedAssemblyRootRemoving(Assembly* a); + + void onNoSimulateAssemblyRootAdded(Assembly* a) {onFixedAssemblyRootAdded(a);} + void onNoSimulateAssemblyRootRemoving(Assembly* a) {onFixedAssemblyRootRemoving(a);} + + void onNoSimulateAssemblyDescendentAdded(Assembly* a); + void onNoSimulateAssemblyDescendentRemoving(Assembly* a); + + void onSimulateAssemblyRootAdded(Assembly* a); + void onSimulateAssemblyRootRemoving(Assembly* a); + + void onSimulateAssemblyDescendentAdded(Assembly* a); + void onSimulateAssemblyDescendentRemoving(Assembly* a); + + Assembly* onEngineChanging(Primitive* p); + void onEngineChanged(Assembly* a); + }; +} // namespace diff --git a/App/v8world/Ball.h b/App/v8world/Ball.h new file mode 100644 index 0000000..65e51a4 --- /dev/null +++ b/App/v8world/Ball.h @@ -0,0 +1,70 @@ +#pragma once + +#include "V8World/Geometry.h" +#include "V8World/GeometryPool.h" +#include "V8World/BulletGeometryPoolObjects.h" + +namespace RBX { + + class Ball : public Geometry { + public: + typedef GeometryPool BulletSphereShapePool; + + private: + typedef Geometry Super; + float realRadius; // in real world units, == size.x/2 + BulletSphereShapePool::Token bulletSphereShape; + + + Matrix3 getMomentSolid(float mass) const; + + /*override*/ void setSize(const G3D::Vector3& _size); + + void updateBulletCollisionData(); + + public: + Ball() : realRadius(0.0) {} + ~Ball() {} + + // Primitive Overrides + /*override*/ virtual bool hitTest(const RbxRay& rayInMe, Vector3& localHitPoint, Vector3& surfaceNormal); + + /*override*/ virtual GeometryType getGeometryType() const {return GEOMETRY_BALL;} + /*override*/ virtual CollideType getCollideType() const {return COLLIDE_BALL;} + + // Real Radius + /*override*/ virtual float getRadius() const {return realRadius;} + + // Real Corner + /*override*/ virtual Vector3 getCenterToCorner(const Matrix3& rotation) const { + return Vector3(realRadius, realRadius, realRadius); + } + + // Moment + /*override*/ virtual Matrix3 getMoment(float mass) const { + return getMomentSolid(mass); + } + + // Volume + /*override*/ float getVolume() const; + + // Dragger support + size_t closestSurfaceToPoint( const Vector3& pointInBody ) const; + Plane getPlaneFromSurface( const size_t surfaceId ) const; + CoordinateFrame getSurfaceCoordInBody( const size_t surfaceId ) const; + Vector3 getSurfaceNormalInBody( const size_t surfaceId ) const; + size_t getMostAlignedSurface( const Vector3& vecInWorld, const G3D::Matrix3& objectR ) const; + int getNumSurfaces( void ) const { return 6; } + Vector3 getSurfaceVertInBody( const size_t surfaceId, const int vertId ) const; + int getNumVertsInSurface( const size_t surfaceId ) const; + bool vertOverlapsFace( const Vector3& pointInBody, const size_t surfaceId ) const; + + bool findTouchingSurfacesConvex( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId ) const {return false;} + bool FacesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const {RBXASSERT(0); return false;} + bool FaceVerticesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const{RBXASSERT(0); return false;} + bool FaceEdgesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const {RBXASSERT(0); return false;} + + /*override*/ bool setUpBulletCollisionData(void); + }; + +} // namespace diff --git a/App/v8world/BallCellContact.h b/App/v8world/BallCellContact.h new file mode 100644 index 0000000..29a201f --- /dev/null +++ b/App/v8world/BallCellContact.h @@ -0,0 +1,46 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/CellContact.h" + +namespace RBX { + + class Poly; + class BallPlaneConnector; + class BallEdgeConnector; + class BallVertexConnector; + + namespace POLY { + class Face; + class Edge; + class Vertex; + } + + class BallCellContact + : public CellMeshContact + , public Allocator + { + private: + const POLY::Face* getFarthestPlane(float& planeToCenter, const Vector3& ballInCell); + const POLY::Edge* getClosestEdge(const POLY::Face* face, float& edgeToCenter, const Vector3& ballInCell); + const POLY::Edge* getClosestInVoronoiEdge(const POLY::Face* face, float& edgeToCenter, const Vector3& ballInCell); + const POLY::Vertex* getClosestVertex(const POLY::Edge* edge, float& vertexToCenter, const Vector3& ballInCell); + + BallPlaneConnector* newBallPlaneConnector(const POLY::Face* face); + BallEdgeConnector* newBallEdgeConnector(const POLY::Edge* edge); + BallVertexConnector* newBallVertexConnector(const POLY::Vertex* vertex); + + const Ball* ball() const; + const Poly* poly() const; + + /*override*/ void findClosestFeatures(ConnectorArray& newConnectors); + public: + BallCellContact(Primitive* p0, Primitive* p1, const Vector3int16& cell); + ~BallCellContact(); + + void generateDataForMovingAssemblyStage(void); /*override*/ + }; + + +} // namespace \ No newline at end of file diff --git a/App/v8world/BallPolyContact.h b/App/v8world/BallPolyContact.h new file mode 100644 index 0000000..eaff4d1 --- /dev/null +++ b/App/v8world/BallPolyContact.h @@ -0,0 +1,44 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/PolyContact.h" + +namespace RBX { + + class Poly; + class BallPlaneConnector; + class BallEdgeConnector; + class BallVertexConnector; + + namespace POLY { + class Face; + class Edge; + class Vertex; + } + + class BallPolyContact + : public PolyContact + , public Allocator + { + private: + const POLY::Face* getFarthestPlane(float& planeToCenter, const Vector3& ballInPoly); + const POLY::Edge* getClosestEdge(const POLY::Face* face, float& edgeToCenter, const Vector3& ballInPoly); + const POLY::Edge* getClosestInVoronoiEdge(const POLY::Face* face, float& edgeToCenter, const Vector3& ballInPoly); + const POLY::Vertex* getClosestVertex(const POLY::Edge* edge, float& vertexToCenter, const Vector3& ballInPoly); + + BallPlaneConnector* newBallPlaneConnector(const POLY::Face* face); + BallEdgeConnector* newBallEdgeConnector(const POLY::Edge* edge); + BallVertexConnector* newBallVertexConnector(const POLY::Vertex* vertex); + + const Ball* ball() const; + const Poly* poly() const; + + /*override*/ void findClosestFeatures(ConnectorArray& newConnectors); + public: + BallPolyContact(Primitive* p0, Primitive* p1); + void generateDataForMovingAssemblyStage(void); /*override*/ + }; + + +} // namespace \ No newline at end of file diff --git a/App/v8world/BasicSpatialHashPrimitive.h b/App/v8world/BasicSpatialHashPrimitive.h new file mode 100644 index 0000000..5116813 --- /dev/null +++ b/App/v8world/BasicSpatialHashPrimitive.h @@ -0,0 +1,68 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "Util/ExtentsInt32.h" +#include "Util/Extents.h" +#include "rbx/Debug.h" + +//#define _RBX_DEBUGGING_SPATIAL_HASH + +#ifdef _RBX_DEBUGGING_SPATIAL_HASH + #define RBXASSERT_SPATIAL_HASH(expr) RBXASSERT(expr) + const bool assertingSpatialHash = true; +#else + #define RBXASSERT_SPATIAL_HASH(expr) ((void)0) + const bool assertingSpatialHash = false; +#endif + +namespace RBX { + + /** use the SpatialHash with classes that contain these members: + * basic Primitive must implement: + */ + class BasicSpatialHashPrimitive + { + private: + ExtentsInt32 oldSpatialExtents; + int spatialNodeLevel; + +#ifdef _RBX_DEBUGGING_SPATIAL_HASH + void* spatialNodes; + int spatialNodeCount; +#endif + + public: + BasicSpatialHashPrimitive() + : spatialNodeLevel(-1) +#ifdef _RBX_DEBUGGING_SPATIAL_HASH + , spatialNodes(0) + , spatialNodeCount(0) +#endif + {}; + + ~BasicSpatialHashPrimitive() + { + RBXASSERT(spatialNodeLevel == -1); // + RBXASSERT_SPATIAL_HASH(spatialNodes == NULL); + RBXASSERT_SPATIAL_HASH(spatialNodeCount == 0); + spatialNodeLevel = -2; + } + + bool IsInSpatialHash() { return spatialNodeLevel > -1;} + + // The remaining functions are used by the SpatialHash<> implementation + int getSpatialNodeLevel() const { + RBXASSERT(spatialNodeLevel >= -1); + return spatialNodeLevel; + } + void setSpatialNodeLevel(int value) {spatialNodeLevel = value;} + + const ExtentsInt32& getOldSpatialExtents() const {return oldSpatialExtents;} + void setOldSpatialExtents(const ExtentsInt32& value) {oldSpatialExtents = value;} + + const Vector3int32& getOldSpatialMin() {return oldSpatialExtents.low;} + const Vector3int32& getOldSpatialMax() {return oldSpatialExtents.high;} + + }; + +} // namespace diff --git a/App/v8world/Block.h b/App/v8world/Block.h new file mode 100644 index 0000000..37fb8c5 --- /dev/null +++ b/App/v8world/Block.h @@ -0,0 +1,139 @@ +#pragma once + +#include "V8World/Poly.h" +#include "V8World/GeometryPool.h" +#include "V8World/BlockCorners.h" +#include "V8World/BlockMesh.h" +#include "V8World/BulletGeometryPoolObjects.h" + +#include "V8Kernel/ContactParams.h" +#include "Util/NormalID.h" +#include "rbx/Debug.h" + +namespace RBX { + + class Block : public Poly { + friend class TriangleMesh; + private: + typedef Poly Super; + public: + typedef GeometryPool BlockMeshPool; + typedef GeometryPool BlockCornersPool; + typedef GeometryPool BulletBoxShapePool; + private: + BlockCornersPool::Token blockCorners; + BlockMeshPool::Token blockMesh; + BulletBoxShapePool::Token bulletBoxShape; + + const Vector3* vertices; // in Real World units, object coords - shortcut to wrapper data + + static const int BLOCK_FACE_TO_VERTEX[6][4]; + static const int BLOCK_FACE_VERTEX_TO_EDGE[6][4]; + + // loading GeoPair stuff + const Vector3* getCornerPoint(const Vector3int16& clip) const; + const Vector3* getEdgePoint(const Vector3int16& clip, NormalId& normalID) const; + const Vector3* getPlanePoint(const Vector3int16& clip, NormalId& normalID) const; + + Matrix3 getMomentHollow(float mass) const; + + /*override*/ void setSize(const G3D::Vector3& _size); + + // Primitive Overrides + /*override*/ virtual bool hitTest(const RbxRay& rayInMe, Vector3& localHitPoint, Vector3& surfaceNormal); + + /*override*/ virtual GeometryType getGeometryType() const {return GEOMETRY_BLOCK;} + /*override*/ virtual CollideType getCollideType() const {return COLLIDE_BLOCK;} + + public: + // Real Corner + /*override*/ virtual Vector3 getCenterToCorner(const Matrix3& rotation) const; + + private: + // Moment + /*override*/ virtual Matrix3 getMoment(float mass) const {return getMomentHollow(mass);} + + // Volume + /*override*/ float getVolume() const; + + // Poly Overrides + /*override*/ void buildMesh(); + + void updateBulletCollisionData(); + + public: + Block() : vertices(NULL) {} + + ~Block() {} + + static void init(); + + /////////////////////////////////////////////////////////////// + // + // Block Specific Collision Detection + void projectToFace(Vector3& ray, Vector3int16& clip, int& onBorder); + + GeoPairType getBallInsideInfo(const Vector3& ray, const Vector3* &offset, + NormalId& normalID); + GeoPairType getBallBlockInfo(int onBorder, const Vector3int16 clip, const Vector3* &offset, + NormalId& normalID); + + inline const float* getVertices() const { + return (float*)vertices; + } + + inline const Vector3& getExtent() const { + return vertices[0]; + } + + const Vector3* getFaceVertex(NormalId faceID, int vertID) const { + return &vertices[ BLOCK_FACE_TO_VERTEX[faceID][vertID] ]; + } + + int getClosestEdge(const Matrix3& rotation, NormalId normalID, const Vector3& crossAxis); + + // tricky - given a face and a vertex on it, find the edge + // assumes that the vertices are in counterclockwise order on the face, + // and gives the edge that connects this vertex with the next one in + // counter-clockwise order + inline int faceVertexToEdge(NormalId faceID, int vertID) { + return BLOCK_FACE_VERTEX_TO_EDGE[faceID][vertID]; + } + + // same as the previsous, but gives the edge that + // connects with the next in clockwise order + inline int faceVertexToClockwiseEdge(NormalId faceID, int vertID) { + return 12 + BLOCK_FACE_VERTEX_TO_EDGE[faceID][vertID]; + } + + const Vector3* getEdgeVertex(int edgeId) const { + if (edgeId < 12) { + return &vertices[ Block::BLOCK_FACE_TO_VERTEX[edgeId / 4][edgeId % 4] ]; + } + else { + int ccwEdge = edgeId - 12; // convert to regular.. + NormalId faceId = (NormalId) (ccwEdge / 4); + RBXASSERT(validNormalId(faceId)); + int vertId = ccwEdge+1 % 4; // one higher - add + return &vertices[ Block::BLOCK_FACE_TO_VERTEX[faceId][vertId] ]; + } + } + + // returns X,-X,X,-X,Y,-Y,Y,-Y,Z,-Z,Z,-Z + inline NormalId getEdgeNormal(int edgeId) { + NormalId ans = static_cast((edgeId / 4) + (3*(edgeId % 2))); + if (edgeId > 12) { + ans = static_cast((ans + 3) % 6); + } + return ans; + } + + Vector2 getProjectedVertex(const Vector3& vertex, NormalId normalID); + + // Currently used by dragger + /*override*/ CoordinateFrame getSurfaceCoordInBody( const size_t surfaceId ) const; + + /*override*/ bool setUpBulletCollisionData(void); + }; + +} // namespace diff --git a/App/v8world/BlockCorners.h b/App/v8world/BlockCorners.h new file mode 100644 index 0000000..1a1aee7 --- /dev/null +++ b/App/v8world/BlockCorners.h @@ -0,0 +1,41 @@ +#pragma once + +#include "Util/Memory.h" + + +/* + Utility class - holds Vector3 [8] so that all blocks of the same size use the same geometry to improve cache / ram size for collisions +*/ + +namespace RBX { + + namespace POLY { + + class BlockCorners : public Allocator + { + private: + Vector3 vertices[8]; + public: + BlockCorners(const Vector3& _corner) + { + Vector3 corner; + corner.x = - std::abs(_corner.x); + corner.y = - std::abs(_corner.y); + corner.z = - std::abs(_corner.z); + + for (int i = 0; i < 2; i++) { + corner.x *= -1.0; // positive for i = 0, negative for i = -1 + for (int j = 0; j < 2; j++) { + corner.y *= -1.0; // positive for j = 0... + for (int k = 0; k < 2; k++) { + corner.z *= -1.0; + vertices[i*4 + j*2 + k] = corner; + } + } + } + } + const Vector3* getVertices() const {return vertices;} + }; + + } // namespace POLY +} // namespace RBX diff --git a/App/v8world/BlockMesh.h b/App/v8world/BlockMesh.h new file mode 100644 index 0000000..4d44030 --- /dev/null +++ b/App/v8world/BlockMesh.h @@ -0,0 +1,25 @@ +#pragma once + +/* + Utility class - holds Block Meshes of same size for use by Geometry Pool. +*/ + +#include "V8World/Mesh.h" +#include "Util/Memory.h" + +namespace RBX { + + namespace POLY { + + class BlockMesh : public Allocator + { + Mesh mesh; + public: + BlockMesh(const Vector3& size) { + mesh.makeBlock(size); + } + const Mesh* getMesh() const {return &mesh;} + }; + + } // namespace POLY +} // namespace RBX diff --git a/App/v8world/BulletContact.h b/App/v8world/BulletContact.h new file mode 100644 index 0000000..c8ca5e5 --- /dev/null +++ b/App/v8world/BulletContact.h @@ -0,0 +1,83 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "v8kernel/ContactConnector.h" +#include "v8world/Contact.h" +#include "v8world/CellContact.h" + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" + +class btPersistentManifold; + +namespace RBX { + +class BulletConnector: public ContactConnector, public Allocator +{ +public: + BulletConnector(Body* b0, Body* b1, const ContactParams& contactParams, int manifoldIndex, int cacheIndex); + + int bulletManifoldIndex; + int bulletPointCacheIndex; +}; + +typedef FixedArray BulletConnectorArray; + +class BulletContact: public Contact +{ +public: + BulletContact(World* world, Primitive* p0, Primitive* p1); + ~BulletContact(); + + // Contact + void deleteAllConnectors() override; + int numConnectors() const override; + ContactConnector* getConnector(int i) override; + + bool computeIsColliding(float overlapIgnored) override; + bool stepContact() override; + + void invalidateContactCache() override; + +private: + World* world; + + btCollisionAlgorithm* algorithm; + + btManifoldArray manifoldArray; + BulletConnectorArray connectors; +}; + +class BulletCellContact: public CellContact +{ +public: + BulletCellContact(World* world, Primitive* p0, Primitive* p1, const Vector3int32& feature, const shared_ptr& cellShape); + ~BulletCellContact(); + + // Contact + void deleteAllConnectors() override; + int numConnectors() const override; + ContactConnector* getConnector(int i) override; + + bool computeIsColliding(float overlapIgnored) override; + bool stepContact() override; + + void invalidateContactCache() override; + void onPrimitiveContactParametersChanged() override; + +private: + World* world; + + btCollisionAlgorithm* algorithm; + + btManifoldArray manifoldArray; + BulletConnectorArray connectors; + + btCollisionObject cellCollisionObject; // collision object for the cell involved in contact + shared_ptr cellShape; + + void updateContactParemeters(btCollisionObject* cellObj); +}; + +} // namespace diff --git a/App/v8world/BulletGeometryPoolObjects.h b/App/v8world/BulletGeometryPoolObjects.h new file mode 100644 index 0000000..4a0143e --- /dev/null +++ b/App/v8world/BulletGeometryPoolObjects.h @@ -0,0 +1,112 @@ +#pragma once + +/* + Utility class - holds Bullet Shapes for use by Geometry Pool. +*/ + +#include "Util/Memory.h" + +#include "BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h" +#include "BulletCollision/CollisionShapes/btConvexHullShape.h" +#include "BulletCollision/CollisionShapes/btConvexPolyhedron.h" +#include "BulletCollision/CollisionShapes/btShapeHull.h" +#include "BulletCollision/GImpact/btGImpactShape.h" +#include "Extras/GIMPACTUtils/btGImpactConvexDecompositionShape.h" +#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" +#include "btBulletCollisionCommon.h" + +// Comment this out to use btCompoundShape and the more robust narrow phase +// Uncomment to use btGImpactConvexDecompositionShape +#define USE_GIMPACT + +const float bulletCollisionMargin = 0.05f; + + + +namespace RBX { + + class BulletDecompWrapper : public Allocator + { + public: + #ifdef USE_GIMPACT + typedef btGImpactConvexDecompositionShape ShapeType; + #else + typedef btCompoundShape ShapeType; + #endif + + struct ConvexExtents + { + Vector3 center; + Vector3 size; + }; + + BulletDecompWrapper(const std::string& str); + ~BulletDecompWrapper(); + + const ShapeType* getCompound() const { return decomp; } + const std::vector& getExtentArray() const { return extentArray; } + + private: + ShapeType* decomp; + std::vector extentArray; + }; + + class BulletBoxShapeWrapper : public Allocator + { + private: + btBoxShape* boxShape; + + public: + const btBoxShape* getShape(void) const { return boxShape; } + BulletBoxShapeWrapper(const Vector3& key); + ~BulletBoxShapeWrapper(); + + }; + + class BulletSphereShapeWrapper : public Allocator + { + private: + btSphereShape* sphereShape; + + public: + const btSphereShape* getShape(void) const { return sphereShape; } + BulletSphereShapeWrapper(const float& key); + ~BulletSphereShapeWrapper(); + + }; + + class BulletCylinderShapeWrapper : public Allocator + { + private: + btCylinderShape* cylinderShape; + + public: + const btCylinderShape* getShape(void) const { return cylinderShape; } + BulletCylinderShapeWrapper(const Vector3& key); + ~BulletCylinderShapeWrapper(); + }; + + class BulletWedgeShapeWrapper : public Allocator + { + private: + btConvexHullShape* wedgeShape; + + public: + const btConvexHullShape* getShape(void) const { return wedgeShape; } + BulletWedgeShapeWrapper(const Vector3& key); + ~BulletWedgeShapeWrapper(); + + }; + + class BulletCornerWedgeShapeWrapper : public Allocator + { + private: + btConvexHullShape* cornerWedgeShape; + + public: + const btConvexHullShape* getShape(void) const { return cornerWedgeShape; } + BulletCornerWedgeShapeWrapper(const Vector3& key); + ~BulletCornerWedgeShapeWrapper(); + + }; +} // namespace RBX diff --git a/App/v8world/BulletShapeCellContact.h b/App/v8world/BulletShapeCellContact.h new file mode 100644 index 0000000..542550b --- /dev/null +++ b/App/v8world/BulletShapeCellContact.h @@ -0,0 +1,72 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/CellContact.h" +#include "V8World/Mesh.h" +#include "Voxel/Util.h" + +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" + + +class btPersistentManifold; +class bulletNPAlgorithm; +class btConvexHullShape; +namespace RBX { + class PolyConnector; + class BulletShapeCellConnector; + class BulletShapeCellContact : public CellMeshContact + { + public: + typedef RBX::FixedArray BulletConnectorArray; + + private: + btCollisionAlgorithm* bulletNPAlgorithm; + + btCollisionObject bulletCollisionObject; // collision object for the cell involved in contact + + shared_ptr customShape; + + BulletConnectorArray polyConnectors; + + World* world; + + void removeAllConnectorsFromKernel(); + void putAllConnectorsInKernel(); + void updateClosestFeatures(); + float worstFeatureOverlap(); + void deleteConnectors(BulletConnectorArray& deleteConnectors); + void matchClosestFeatures(BulletConnectorArray& newConnectors); + BulletShapeCellConnector* matchClosestFeature(BulletShapeCellConnector* newConnector); + // Terrain Materials + void updateContactParemeters(btCollisionObject* cellObj, BulletConnectorArray& connectors); + + + // use a BulletShapeConnector to represent this connector (we don't need a specific BulletShapeCellConnector) + BulletShapeCellConnector* newBulletShapeCellConnector(btCollisionObject* bulletColObj0, btCollisionObject* bulletColObj1, + btCollisionAlgorithm* algo, int manifoldIndex, int contactIndex); + + void updateContactPoints(); + void computeManifoldsWithBulletNarrowPhase(btManifoldArray& manifoldArray); + + // Contact + void deleteAllConnectors() override; + int numConnectors() const override {return polyConnectors.size();} + ContactConnector* getConnector(int i) override; + bool computeIsColliding(float overlapIgnored) override; + bool stepContact() override; + + void invalidateContactCache() override; + + void findClosestFeatures(ConnectorArray& newConnectors) override {RBXASSERT(0);} // don't use this when using btCompound Narrow Phase + // since it generates too many connectors + void findClosestBulletCellFeatures(BulletConnectorArray& newConnectors); + + public: + BulletShapeCellContact(Primitive* p0, Primitive* p1, const Vector3int16& cell, World* contactWorld); + BulletShapeCellContact(Primitive* p0, Primitive* p1, const Vector3int32& feature, const shared_ptr& customShape, World* contactWorld); + ~BulletShapeCellContact(); + }; +} // namespace diff --git a/App/v8world/BulletShapeContact.h b/App/v8world/BulletShapeContact.h new file mode 100644 index 0000000..9445b7b --- /dev/null +++ b/App/v8world/BulletShapeContact.h @@ -0,0 +1,55 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/Contact.h" +#include "V8World/Mesh.h" +#include "Voxel/Util.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" + +class btPersistentManifold; +class bulletNPAlgorithm; +class btConvexHullShape; +namespace RBX { + class PolyConnector; + class BulletShapeConnector; + class BulletShapeContact : public Contact + { + public: + typedef RBX::FixedArray BulletConnectorArray; + + private: + btPersistentManifold* bulletManifold; + btCollisionAlgorithm* bulletNPAlgorithm; + BulletConnectorArray polyConnectors; + World* world; + + void removeAllConnectorsFromKernel(); + void putAllConnectorsInKernel(); + void updateClosestFeatures(); + float worstFeatureOverlap(); + void matchClosestFeatures(BulletConnectorArray& newConnectors); + BulletShapeConnector* matchClosestFeature(BulletShapeConnector* newConnector); + void deleteConnectors(BulletConnectorArray& deleteConnectors); + BulletShapeConnector* newBulletShapeConnector(btCollisionObject* bulletColObj0, btCollisionObject* bulletColObj1, + btCollisionAlgorithm* algo, int manifoldIndex, int contactIndex, bool swapped); + void updateContactPoints(); + void computeManifoldsWithBulletNarrowPhase(btManifoldArray& manifoldArray); + + // Contact + void deleteAllConnectors() override; + int numConnectors() const override {return polyConnectors.size();} + ContactConnector* getConnector(int i) override; + bool computeIsColliding(float overlapIgnored) override; + bool stepContact() override; + + void invalidateContactCache() override; + + /*implement*/ void findClosestFeatures(BulletConnectorArray& newConnectors); + + public: + BulletShapeContact(Primitive* p0, Primitive* p1, World* ourWorld); + ~BulletShapeContact(); + }; +} // namespace diff --git a/App/v8world/Buoyancy.h b/App/v8world/Buoyancy.h new file mode 100644 index 0000000..01213a6 --- /dev/null +++ b/App/v8world/Buoyancy.h @@ -0,0 +1,181 @@ +#pragma once + +#include "Voxel/Cell.h" +#include "v8kernel/BuoyancyConnector.h" +#include "v8World/Geometry.h" +#include "v8World/Contact.h" + +/* + The Buoyancy feature manages all aspects of parts' interaction with water when they have come + into contact with water. + + The Buoyancy contact is implemented as a standard contact type managed by ContactManager. + + Each BuoyancyContact manages a few BuoyancyConnectors that represent the buoyancy and water + viscosity forces applied on the part. + + Box Buoyancy is divided into 8 voxels, each voxel contributes one connector that represents + the buoyancy and viscosity force applied on that voxel shape. +*/ + +namespace RBX { + + namespace Voxel { class Grid; } + namespace Voxel2 { class Grid; } + + class BuoyancyContact : public Contact + { + public: + static const int MAX_CONNECTORS = 8; + static float waterViscosity; + static const float waterDensity; + + typedef RBX::FixedArray ConnectorArray; + + static Geometry::GeometryType determineGeometricType( Primitive *prim ); + static BuoyancyContact* create( Primitive* p0, Primitive *p1 ); + + BuoyancyContact( Primitive* p0, Primitive* p1 ); + ~BuoyancyContact(); + + virtual Geometry::GeometryType getType() = 0; + + ContactType getContactType() const override { return Contact_Buoyancy; } + + void onPrimitiveContactParametersChanged() override; + + private: + void deleteConnectors(); + + void updateBuoyancyFloatingForce(); + + protected: + ConnectorArray connectors; + Primitive* floaterPrim; + + Voxel::Grid* voxelGrid; + Voxel2::Grid* smoothGrid; + + float radius; + float fullSurfaceArea; + Vector3 fullBuoyancy; + + bool worldPosUnderWater( const Vector3& pos ); + bool isTouchingWater( Primitive* prim ); + + Voxel::Cell getWaterCell( Vector3int16 pos ); + bool cellHasWater( Vector3int16 pos ); + + bool hasDistanceSubmergedUnderWater( const Vector3& worldpos, float& waterLevel, const Vector3& searchEnd ); + bool worldPosAboveWater( const Vector3& worldpos, int minY, float& waterLevel ); + Vector3 cellVelocity( const Vector3& worldpos ); + + void removeAllConnectorsFromKernel(); + void putAllConnectorsInKernel(); + void computeExtentsWaterBand( const Extents& extents, float& floatDistance, float& sinkDistance ); + void updateConnectors(); + + // Contact API + void deleteAllConnectors() override; + int numConnectors() const override { return connectors.size(); } + ContactConnector* getConnector( int i ) override { return connectors[i]; } + bool stepContact() override; + bool computeIsColliding(float overlapIgnored) override; + bool computeIsCollidingUi(float overlapIgnored) override; // override to always return false so can build underwater; shouldn't affect HumanoidState code + + // Buoyancy Shape API + virtual Vector3 getWaterVelocity(int i); + virtual void createConnectors() = 0; + virtual void updateWaterBand() = 0; + virtual void updateSubmergeRatio(); + virtual void getSurfaceAreaInDirection(const Vector3& relativeVelocity, float& crossArea, float& tangentArea) = 0; + virtual void initializeCrossSections() = 0; + virtual Vector3 getCrossSections(int i, const Vector3& velocity) = 0; + }; + + /////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + + class BuoyancyBallContact : public BuoyancyContact + { + + protected: + float crossSectionArea; + + bool computeIsColliding(float overlapIgnored); + void createConnectors(); + Vector3 getWaterVelocity(int i); + void updateWaterBand(); + void updateSubmergeRatio(); + void getSurfaceAreaInDirection(const Vector3& relativeVelocity, float& crossArea, float& tangentArea); + void initializeCrossSections(); + virtual Vector3 getCrossSections(int, const Vector3&); + + public: + BuoyancyBallContact( Primitive* p0, Primitive* p1 ) : BuoyancyContact(p0, p1) {} + Geometry::GeometryType getType() { return Geometry::GEOMETRY_BALL; } + }; + + /////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + + class BuoyancyBoxContact : public BuoyancyContact + { + + protected: + Vector3 crossSectionSurfaceAreas; + Vector3 tangentSurfaceAreas; + + void createConnectors(); + void updateWaterBand(); + virtual void getSurfaceAreaInDirection(const Vector3& relativeVelocity, float& crossSectionArea, float& tangentSurfaceAread); + virtual void initializeCrossSections(); + Vector3 getCrossSections( int i, const Vector3& velocity ); + public: + BuoyancyBoxContact( Primitive* p0, Primitive* p1 ); + Geometry::GeometryType getType() { return Geometry::GEOMETRY_BLOCK; } + }; + + /////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + + class BuoyancyCylinderContact : public BuoyancyBoxContact + { + protected: + void updateSubmergeRatio(); + void initializeCrossSections(); + + public: + BuoyancyCylinderContact( Primitive* p0, Primitive* p1 ) : BuoyancyBoxContact(p0, p1) {} + Geometry::GeometryType getType() { return Geometry::GEOMETRY_CYLINDER; } + }; + + /////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + + class BuoyancyWedgeContact : public BuoyancyBoxContact + { + + protected: + void updateSubmergeRatio(); + void initializeCrossSections(); + + public: + BuoyancyWedgeContact( Primitive* p0, Primitive* p1 ) : BuoyancyBoxContact(p0, p1) {} + Geometry::GeometryType getType() { return Geometry::GEOMETRY_WEDGE; } + }; + + /////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + + class BuoyancyCornerWedgeContact : public BuoyancyBoxContact + { + protected: + void updateSubmergeRatio(); + void initializeCrossSections(); + + public: + BuoyancyCornerWedgeContact( Primitive* p0, Primitive* p1 ) : BuoyancyBoxContact(p0, p1) {} + Geometry::GeometryType getType() { return Geometry::GEOMETRY_CORNERWEDGE; } + }; +} diff --git a/App/v8world/CellContact.h b/App/v8world/CellContact.h new file mode 100644 index 0000000..31cbd7a --- /dev/null +++ b/App/v8world/CellContact.h @@ -0,0 +1,88 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/Contact.h" +#include "V8World/Mesh.h" +#include "Voxel/Util.h" +#include "Util/Vector3int32.h" + +namespace RBX { + + class PolyConnector; + + const Vector3int16 kFaceDirectionToLocationOffset[7] = + { + Vector3int16( 1, 0, 0), + Vector3int16( 0, 0, 1), + Vector3int16(-1, 0, 0), + Vector3int16( 0, 0,-1), + Vector3int16( 0, 1, 0), + Vector3int16( 0,-1, 0), + Vector3int16( 0, 0, 0), + }; + + static inline Voxel::FaceDirection oppositeSideOffset(Voxel::FaceDirection f) + { + static Voxel::FaceDirection OPPOSITES[6] = { Voxel::MinusX, Voxel::MinusZ, Voxel::PlusX, Voxel::PlusZ, Voxel::MinusY, Voxel::PlusY }; + return OPPOSITES[f]; + } + + class CellContact: public Contact + { + public: + CellContact(Primitive* p0, Primitive* p1, const Vector3int32& gridFeature) + : Contact(p0, p1) + , gridFeature(gridFeature) + {} + + const Vector3int32& getGridFeature() const { return gridFeature; } + + virtual ContactType getContactType() const { return Contact_Cell; } + + protected: + Vector3int32 gridFeature; + }; + + class CellMeshContact: public CellContact + { + public: + typedef RBX::FixedArray ConnectorArray; // TODO - should only ever need 8 + + protected: + POLY::Mesh* cellMesh; + + private: + ConnectorArray polyConnectors; + + void removeAllConnectorsFromKernel(); + void putAllConnectorsInKernel(); + void updateClosestFeatures(); + float worstFeatureOverlap(); + void deleteConnectors(ConnectorArray& deleteConnectors); + void matchClosestFeatures(ConnectorArray& newConnectors); + PolyConnector* matchClosestFeature(PolyConnector* newConnector); + void updateContactPoints(); + + // Contact + /*override*/ void deleteAllConnectors(); + /*override*/ int numConnectors() const {return polyConnectors.size();} + /*override*/ ContactConnector* getConnector(int i); + /*override*/ bool computeIsColliding(float overlapIgnored); + /*override*/ bool stepContact(); + + /*implement*/ virtual void findClosestFeatures(ConnectorArray& newConnectors) = 0; + + public: + CellMeshContact(Primitive* p0, Primitive* p1, const Vector3int32& gridFeature) + : CellContact(p0, p1, gridFeature) + , cellMesh(NULL) + {} + + ~CellMeshContact(); + + POLY::Mesh* getCellMesh(void) {return cellMesh;} + + bool cellFaceIsInterior(const Vector3int16& mainCellLoc, RBX::Voxel::FaceDirection faceDir); + }; +} // namespace diff --git a/App/v8world/CleanStage.h b/App/v8world/CleanStage.h new file mode 100644 index 0000000..c7d0d7d --- /dev/null +++ b/App/v8world/CleanStage.h @@ -0,0 +1,49 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" +#include + +namespace RBX { + + class Joint; + class Primitive; + /* + Sits between the world and the JointStage (for now, the engine); + + Receives: + 1) Primitives + 2) Edges (Joints and Contacts) + + Passes Downstream: + 1) Primitives + 2) Edges + a) between two primitives, both different, both non-null + */ + + class CleanStage : public IWorldStage { + private: + class JointStage* getJointStage(); + + bool primitivesAreOk(Edge* e); + + public: + /////////////////////////////////////////// + // IStage + CleanStage(IStage* upstream, World* world); + + ~CleanStage() {} + + /*override*/ IStage::StageType getStageType() const {return IStage::CLEAN_STAGE;} + + /*override*/ void onEdgeAdded(Edge* e); + /*override*/ void onEdgeRemoving(Edge* e); + + void onPrimitiveAdded(Primitive* p); + void onPrimitiveRemoving(Primitive* p); + + void onJointPrimitiveNulling(Joint* j, Primitive* nulling); + void onJointPrimitiveSet(Joint* j, Primitive* p); + }; +} // namespace \ No newline at end of file diff --git a/App/v8world/Clump.h b/App/v8world/Clump.h new file mode 100644 index 0000000..03edfb7 --- /dev/null +++ b/App/v8world/Clump.h @@ -0,0 +1,63 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "rbx/Debug.h" +#include "Util/IndexedMesh.h" +#include "Primitive.h" +#include + +namespace RBX { + + class Edge; + class Joint; + class Clump; + class PrimIterator; + + class Clump + : public boost::noncopyable + , public IndexedMesh + { + private: + template + inline void visitPrimitivesImpl(Func func, Primitive* p) { + func(p); + for (int i = 0; i < p->numChildren(); ++i) { + Primitive* child = p->getTypedChild(i); + if (!Clump::isClumpRootPrimitive(child)) { + visitPrimitivesImpl(func, child); + } + } + } + + public: + Clump(); + + ~Clump(); + + Clump* getRootClump() {return getRoot();} + const Clump* getRootClump() const {return getRoot();} + + Primitive* getClumpPrimitive() {return rbx_static_cast(getLower());} + const Primitive* getConstClumpPrimitive() const {return rbx_static_cast(getConstLower());} + + static Clump* getPrimitiveClump(Primitive* p); + static const Clump* getConstPrimitiveClump(const Primitive* p); + static bool isClumpRootPrimitive(const Primitive* p); + + void loadMotors(G3D::Array& load, bool nonAnimatedOnly); + void loadConstMotors(G3D::Array& load, bool nonAnimatedOnly) const; + + template + inline void visitPrimitives(Func func) { + Primitive* p = getClumpPrimitive(); + RBXASSERT(p); + visitPrimitivesImpl(func, p); + } + }; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +} // namespace diff --git a/App/v8world/Contact.h b/App/v8world/Contact.h new file mode 100644 index 0000000..041888b --- /dev/null +++ b/App/v8world/Contact.h @@ -0,0 +1,327 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/Edge.h" +#include "V8World/Feature.h" +#include "V8Kernel/ContactParams.h" +#include "Util/G3DCore.h" +#include "Util/Memory.h" +#include "Util/NormalId.h" +#include "Util/Math.h" +#include "Util/FixedArray.h" +#include "rbx/Debug.h" +#include "rbx/Declarations.h" + +#define CONTACT_ARRAY_SIZE 40 +#define BULLET_CONTACT_ARRAY_SIZE 40 + +namespace RBX { + + class Kernel; + class CollisionStage; + class ContactConnector; + class GeoPairConnector; + class BallBlockConnector; + class BallBallConnector; + class Body; + class Ball; + class Block; + class BlockBlockContactData; + + class RBXBaseClass Contact : public Edge + { + private: + typedef Edge Super; + + friend class CollisionStage; + + + static bool ignoreBool; + + // CollisionStage + int lastUiContactStep; + int steppingIndex; // For fast removal from the collision stage stepping list + short numTouchCycles; + + ///////////////////////////////////////////////////// + // + // Edge + + /*override*/ void putInKernel(Kernel* _kernel) { + Super::putInKernel(_kernel); + } + + /*override*/ void removeFromKernel() { + RBXASSERT(getKernel()); + deleteAllConnectors(); + Super::removeFromKernel(); + } + + /////////////////////////////////////////////////// + // Edge Virtuals + // + /*override*/ virtual EdgeType getEdgeType() const {return Edge::CONTACT;} + + protected: + ContactParams* contactParams; + Body* getBody(int i); + + ///////////////////////////////////////////////////// + // + // ContactPairData management + + void deleteConnector(ContactConnector* c); + + virtual void deleteAllConnectors() = 0; // everyone implements this + + virtual bool stepContact() = 0; + + public: + enum ContactType + { + Contact_Simple, + Contact_Cell, + Contact_Buoyancy, + }; + + Contact(Primitive* p0, Primitive* p1); + + virtual ~Contact(); + + short getNumTouchCycles() {return numTouchCycles;} + + int& steppingIndexFunc() {return steppingIndex;} // fast removal from stepping list + + // Proximite tests - compute + typedef bool (Contact::*ProximityTest)(float); + + bool computeIsAdjacentUi(float spaceAllowed); + + virtual bool computeIsCollidingUi(float overlapIgnored); + + virtual bool computeIsColliding(float overlapIgnored) = 0; + + static bool isContact(Edge* e) {return (e->getEdgeType() == Edge::CONTACT);} + + ///////////////////////////////////////////////////// + // + // From The Contact Manager + virtual void onPrimitiveContactParametersChanged(); + + bool step(int uiStepId); + + virtual int numConnectors() const = 0; + + virtual ContactConnector* getConnector(int i) = 0; + + void primitiveMovedExternally(); + + virtual void generateDataForMovingAssemblyStage(void); + + virtual void invalidateContactCache(); + + ContactParams* getContactParams(void) { return contactParams; } + + bool isInContact() { return lastUiContactStep > 0; } + + virtual ContactType getContactType() const { return Contact_Simple; } + }; + + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + + class BallBallContact + : public Contact + , public Allocator + { + private: + BallBallConnector* ballBallConnector; + + Ball* ball(int i); + + /*override*/ void deleteAllConnectors(); + /*override*/ bool computeIsColliding(float overlapIgnored); + /*override*/ bool stepContact(); + + /*override*/ int numConnectors() const {return ballBallConnector ? 1 : 0;} + /*override*/ ContactConnector* getConnector(int i); + + public: + BallBallContact(Primitive* p0, Primitive* p1) + : Contact(p0, p1) + , ballBallConnector(NULL) + {} + + ~BallBallContact() {RBXASSERT(!ballBallConnector);} + + void generateDataForMovingAssemblyStage(void); /*override*/ + }; + + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + class BallBlockContact + : public Contact + , public Allocator + { + private: + BallBlockConnector* ballBlockConnector; + + Primitive* ballPrim(); + Primitive* blockPrim(); + + Ball* ball(); + Block* block(); + + bool computeIsColliding( + int& onBorder, + Vector3int16& clip, + Vector3& projectionInBlock, + float overlapIgnored); + + /*override*/ void deleteAllConnectors(); + /*override*/ bool computeIsColliding(float overlapIgnored); + /*override*/ bool stepContact(); + + /*override*/ int numConnectors() const {return ballBlockConnector ? 1 : 0;} + /*override*/ ContactConnector* getConnector(int i); + + public: + BallBlockContact(Primitive* p0, Primitive* p1) + : Contact(p0, p1) + , ballBlockConnector(NULL) + {} + + ~BallBlockContact() {RBXASSERT(!ballBlockConnector);} + + void generateDataForMovingAssemblyStage(void); /*override*/ + }; + + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + + class BlockBlockContact + : public Contact + , public Allocator + { + private: + static int pairMatches; + static int pairMisses; + static int featureMatches; + static int featureMisses; + + typedef RBX::FixedArray ConnectorArray; + + friend class BlockBlockContactData; + BlockBlockContactData* myData; + + Block* block(int i); + + GeoPairConnector* findGeoPairConnector( + Body* b0, + Body* b1, + GeoPairType _pairType, + int param0, + int param1); + + void loadGeoPairEdgeEdge( + int b0, + int b1, + int edge0, + int edge1); + + void loadGeoPairPointPlane( + int pointBody, + int planeBody, int pointID, + NormalId pointFaceID, + NormalId planeFaceID); + + // plane contact - first compute the feature0, feature1 + bool getBestPlaneEdge(float overlapIgnored, bool& planeContact); + + int intersectRectQuad(Vector2& planeRect, Vector2 (&otherQuad)[4]); + + bool computeIsColliding(float overlapIgnored, bool& planeContact); + + //////////////////////////////////////////////////// + // Contact + /*override*/ void deleteAllConnectors(void); + /*override*/ bool computeIsColliding(float overlapIgnored); + /*override*/ bool stepContact(); + + /*override*/ int numConnectors() const; + /*override*/ ContactConnector* getConnector(int i); + + public: + BlockBlockContact(Primitive* p0, Primitive* p1); + ~BlockBlockContact(); + + static float pairHitRatio(); + static float featureHitRatio(); + + void generateDataForMovingAssemblyStage(void); /*override*/ + + private: + inline static void boxProjection(const Vector3& normal0, const Matrix3& R1, const Vector3& extent1, float& projectedExtent) + { + Vector3 temp = Math::vectorToObjectSpace(normal0, R1); + projectedExtent = std::abs( extent1[0] * temp[0] ) + + std::abs( extent1[1] * temp[1] ) + + std::abs( extent1[2] * temp[2] ); + } + + // if length < 0, no overlap, and no block/block contact + // proj0 >0 on entry + // proj1 >0 on entry + + inline static bool updateBestAxis(float proj0, float p0p1, float proj1, float& _overlap, float overlapIgnored) + { + // no overlap along this axis - bail, not in contact + _overlap = proj0 + proj1 - std::abs(p0p1); + return (_overlap > overlapIgnored); + } + + bool geoFeaturesOverlap( + int pointBody, + int planeBody, + int pointID, + NormalId pointFaceID, + NormalId planeFaceID); + }; + + class BlockBlockContactData + { + friend class BlockBlockContact; + + private: + BlockBlockContact::ConnectorArray connectors[2]; + int connectorsIndex; + + // for hysteresis: + int witnessId; + int separatingAxisId; + + int feature[2]; // -1 no feature, 0..5 plane, 6..8 edge Normal, + int bPlane; + int bOther; + RBX::NormalId planeID; + RBX::NormalId otherPlaneID; + + BlockBlockContact* myOwner; + + public: + BlockBlockContactData(BlockBlockContact* owner); + ~BlockBlockContactData() {} + + int numConnectors() const { return connectors[connectorsIndex].size(); } + ContactConnector* getConnector( int i ); + void clearConnectors( void ); + GeoPairConnector* findGeoPairConnector( Body* b0, Body* b1, GeoPairType _pairType, int param0, int param1 ); + bool stepContact(); + void loadGeoPairEdgeEdgePlane( int edgeBody, int planeBody, int edge0, int edge1 ); + bool getBestPlaneEdge(float overlapIgnored, bool& planeContact); + int computePlaneContact(void); + int intersectRectQuad(Vector2& planeRect, Vector2 (&otherQuad)[4]); + }; + +} // namespace diff --git a/App/v8world/ContactManager.h b/App/v8world/ContactManager.h new file mode 100644 index 0000000..afb53e9 --- /dev/null +++ b/App/v8world/ContactManager.h @@ -0,0 +1,209 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "G3D/Array.h" +#include "Util/ConcurrencyValidator.h" +#include "Util/G3DCore.h" +#include "Util/HitTestFilter.h" +#include "Util/SpatialRegion.h" +#include "Util/SystemAddress.h" +#include "Voxel/CellChangeListener.h" +#include "Util/Extents.h" +#include "Util/Region3.h" +#include "Voxel2/GridListener.h" +#include "v8world/TerrainPartition.h" +#include "v8tree/Instance.h" +#include "util/PartMaterial.h" + +#include "rbx/DenseHash.h" + +#include +#include +#include + +namespace RBX { +namespace Graphics { +class CullableSceneNode; +} } + +namespace RBX { + + namespace Profiling + { + class CodeProfiler; + } + + class Primitive; + class Contact; + class Joint; + class World; + class ContactManagerSpatialHash; + class MegaClusterInstance; + + namespace Voxel { class Grid; } + namespace Voxel2 { class Grid; } + + class ContactManager: + public Voxel::CellChangeListener, + public Voxel2::GridListener + { + ConcurrencyValidator concurrencyValidator; + ContactManagerSpatialHash* spatialHash; + Primitive* myMegaClusterPrim; + World* world; + + typedef boost::unordered_set UpdatedTerrainRegionsSet; + UpdatedTerrainRegionsSet updatedTerrainRegions; + + typedef boost::unordered_set UpdatedTerrainChunksSet; + UpdatedTerrainChunksSet updatedTerrainChunks; + + std::vector tempChunks; + std::vector tempPrimitives; + + static Vector3 dummySurfaceNormal; + static PartMaterial dummySurfaceMaterial; + + Contact* createContact(Primitive* p0, Primitive* p1); + + // TODO: All private and public *Hit() methods have too many arguments! + // Refactor to use struct to bundle arguments + Primitive* getSlowHit( const G3D::Array& primitives, + const RbxRay& unitRay, + const G3D::Array& ignorePrim, + const HitTestFilter* filter, + Vector3& hitPointWorld, + Vector3& surfaceNormal, + PartMaterial& surfaceMaterial, + float maxDistance, + bool& stopped) const; + + Primitive* getFastHit( const RbxRay& worldRay, // implies distance as well + const G3D::Array& ignorePrim, // set to NULL to not use + const HitTestFilter* filter, // set to NULL to not use + Vector3& hitPointWorld, + bool& stopped, + bool terrainCellsAreCubes, + bool ignoreWater, + Vector3& surfaceNormal, + PartMaterial& surfaceMaterial) const; + + /*override*/ virtual void terrainCellChanged(const Voxel::CellChangeInfo& info); + /*override*/ virtual void onTerrainRegionChanged(const Voxel2::Region& region); + + bool checkMegaClusterWaterContact(Primitive* p, const Vector3int16& extentStart, + const Vector3int16& extentEnd, const Vector3int16& extentSize); + bool checkMegaClusterSmallTerrainContact(Primitive* otherPrim, const Vector3int16& extentStart, + const Vector3int16& extentEnd, const Vector3int16& extentSize, + bool cellChanged); + bool checkMegaClusterBigTerrainContact(Primitive* p); + void checkMegaClusterContact(Primitive* p, bool checkTerrain, bool checkWater, bool cellChanged); + + void applyDeferredMegaClusterChanges(); + + bool checkSmoothClusterSolidContact(Primitive* p); + bool checkSmoothClusterWaterContact(Primitive* p); + void checkSmoothClusterContact(Primitive* p, bool cellChanged); + + void applyDeferredSmoothClusterChanges(); + + bool setUpbulletCollisionShapes(Primitive* p0, Primitive* p1); + + Voxel::Grid* getVoxelGrid(); + Voxel2::Grid* getSmoothGrid(); + + public: + + ContactManager(World* world); + ~ContactManager(); + + ///////////////////////////////////////////// + // General Inquiry + // + ContactManagerSpatialHash* getSpatialHash() {return spatialHash;} + + // Returns NULL on no hit + Primitive* getHit( const RbxRay& worldRay, + const std::vector* ignorePrim, // set to NULL to not use + const HitTestFilter* filter, // set to NULL to not use + Vector3& hitPointWorld, + bool terrainCellsAreCubes = false, + bool ignoreWater = false, + Vector3& surfaceNormal = dummySurfaceNormal, + PartMaterial& surfaceMaterial = dummySurfaceMaterial) const; + + void getPrimitivesTouchingExtents( + const Extents& extents, + const Primitive* ignore, + int maxCount, + G3D::Array& found); + + void getPrimitivesTouchingExtents( + const Extents& extents, + const boost::unordered_set& ignorePrimitives, + int maxCount, + G3D::Array& found); + + void getPrimitivesOverlapping(const Extents& extents, DenseHashSet& result); + + bool intersectingGroundPlane(const G3D::Array& check, float yHeight); + bool intersectingOthers(Primitive* check, float overlapIgnored); + bool intersectingOthers(const G3D::Array& check, float overlapIgnored); + bool intersectingOthers(Primitive* check, const std::set& checkSet, float overlapIgnored); + bool intersectingMySimulation(Primitive* check, RBX::SystemAddress myLocalAddress, float overlapIgnored); + + shared_ptr getPartCollisions(Primitive* check); + + ///////////////////////////////////////////// + // From the collision engine + // + void onNewPair(Primitive* p0, Primitive* p1); + void onNewPair(RBX::Graphics::CullableSceneNode* p0, RBX::Graphics::CullableSceneNode* p1) { RBXASSERT(0); } + + void checkTerrainContact(Primitive* p); + void checkTerrainContact(RBX::Graphics::CullableSceneNode* p0) {} + + bool primitiveIsExcludedFromSpatialHash(Primitive* p); + bool primitiveIsExcludedFromSpatialHash(RBX::Graphics::CullableSceneNode* p0) {return false;} + + void releasePair(Primitive* p0, Primitive* p1); + void releasePair(RBX::Graphics::CullableSceneNode* p0, RBX::Graphics::CullableSceneNode* p1) { RBXASSERT(0); } + + ///////////////////////////////////////////// + // From the world + // + void onPrimitiveAdded(Primitive* p); + void onPrimitiveRemoved(Primitive* p); + void onPrimitiveExtentsChanged(Primitive* p); + void onPrimitiveGeometryChanged(Primitive* p); + void onPrimitiveAssembled(Primitive* p); + + void onAssemblyMovedFromStep(Assembly& a); + + void applyDeferredTerrainChanges(); + + void fastClear(); + void doStats(); // spit out hash stats + + /////////////////////////////////////////// + // Profiler + boost::scoped_ptr profilingBroadphase; + + ///////////////////////////////////////////// + // LEGACY + Primitive* getHitLegacy( const RbxRay& originDirection, + const Primitive* ignorePrim, // set to NULL to not use + const HitTestFilter* filter, // set to NULL to not use + Vector3& hitPointWorld, + float& distanceToHit, + const float& maxSearchDepth, + bool ignoreWater) const; + + Primitive* getMegaClusterPrimitive( void ) const { return myMegaClusterPrim; } + + bool terrainCellsInRegion3(Region3 region) const; + Vector3 findUpNearestLocationWithSpaceNeeded(const float maxSearchDepth, const Vector3 &startCenter, const Vector3 &spaceNeededToCorner); + }; + +} // namespace diff --git a/App/v8world/ContactManagerSpatialHash.h b/App/v8world/ContactManagerSpatialHash.h new file mode 100644 index 0000000..25befea --- /dev/null +++ b/App/v8world/ContactManagerSpatialHash.h @@ -0,0 +1,24 @@ + /* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/SpatialHashMultiRes.h" + +namespace RBX +{ + class Primitive; + class Contact; + class ContactManager; + class World; + class Assembly; + +#define CONTACTMANAGER_MAXLEVELS 4 + class ContactManagerSpatialHash : public SpatialHash + { + public: + ContactManagerSpatialHash(World* world, ContactManager* contactManager); + }; + +} + + diff --git a/App/v8world/ContactStage.h b/App/v8world/ContactStage.h new file mode 100644 index 0000000..5eca7fb --- /dev/null +++ b/App/v8world/ContactStage.h @@ -0,0 +1,30 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" + +namespace RBX { + + class Primitive; + + class ContactStage : public IWorldStage { + private: + class TreeStage* getTreeStage(); + + public: + /////////////////////////////////////////// + // IStage + ContactStage(IStage* upstream, World* world); + + ~ContactStage() {} + + /*override*/ IStage::StageType getStageType() const {return IStage::CONTACT_STAGE;} + + /*override*/ void onEdgeAdded(Edge* e); + /*override*/ void onEdgeRemoving(Edge* e); + + void onPrimitiveAdded(Primitive* p); + void onPrimitiveRemoving(Primitive* p); + }; +} // namespace \ No newline at end of file diff --git a/App/v8world/Controller.h b/App/v8world/Controller.h new file mode 100644 index 0000000..e01d821 --- /dev/null +++ b/App/v8world/Controller.h @@ -0,0 +1,29 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +namespace RBX { + + class LegacyController + { + public: + typedef enum InputType {NO_INPUT = 0, + LEFT_TRACK_INPUT, + RIGHT_TRACK_INPUT, + RIGHT_LEFT_INPUT, // -1.0 == right, 1.0 == left + BACK_FORWARD_INPUT, // -1.0 == back, 1.0 == forward + STRAFE_INPUT, + UP_DOWN_INPUT, + BUTTON_1_INPUT, + BUTTON_2_INPUT, + BUTTON_3_INPUT, + BUTTON_4_INPUT, + BUTTON_3_4_INPUT, + CONSTANT_INPUT, + SIN_INPUT, + NUM_INPUT_TYPES} InputType; + // If you add more items here, + // please update the associated string matrix in ControllerTypes.cpp + }; + +} // namespace diff --git a/App/v8world/CornerWedgeMesh.h b/App/v8world/CornerWedgeMesh.h new file mode 100644 index 0000000..fc184fa --- /dev/null +++ b/App/v8world/CornerWedgeMesh.h @@ -0,0 +1,31 @@ +#pragma once + +/* + Utility class - holds CornerWedge Meshes of same size for use by Geometry Pool. +*/ + +#include "Util/Memory.h" +#include "V8World/Mesh.h" + + +namespace RBX { + + namespace POLY { + + class CornerWedgeMesh : public Allocator + { + private: + Mesh mesh; + Vector3 LocalCofM; + + public: + CornerWedgeMesh(const Vector3& size) + { + mesh.makeCornerWedge(size, LocalCofM); + } + const Mesh* getMesh() const {return &mesh;} + const Vector3& GetLocalCofMFromMesh() const { return LocalCofM; } + }; + + } // namespace POLY +} // namespace RBX \ No newline at end of file diff --git a/App/v8world/CornerWedgePoly.h b/App/v8world/CornerWedgePoly.h new file mode 100644 index 0000000..34cc84a --- /dev/null +++ b/App/v8world/CornerWedgePoly.h @@ -0,0 +1,43 @@ +#pragma once + +#include "V8World/Poly.h" +#include "V8World/GeometryPool.h" +#include "V8World/CornerWedgeMesh.h" +#include "V8World/BlockMesh.h" +#include "V8World/BulletGeometryPoolObjects.h" + +namespace RBX { + + class CornerWedgePoly : public Poly { + public: + typedef GeometryPool CornerWedgeMeshPool; + typedef GeometryPool BulletCornerWedgeShapePool; + + /*override*/ Matrix3 getMoment(float mass) const; + /*override*/ Vector3 getCofmOffset() const; + /*override*/ CoordinateFrame getSurfaceCoordInBody( const size_t surfaceId ) const; + /*override*/ bool isGeometryOrthogonal( void ) const { return false; } + /*override*/ bool setUpBulletCollisionData(void); + /*override*/ void setSize(const G3D::Vector3& _size); + + private: + typedef Poly Super; + + CornerWedgeMeshPool::Token aCornerWedgeMesh; + BulletCornerWedgeShapePool::Token bulletCornerWedgeShape; + + /*override*/ virtual Vector3 getCenterToCorner(const Matrix3& rotation) const; + + void updateBulletCollisionData(); + + protected: + // Geometry Overrides + /*override*/ virtual GeometryType getGeometryType() const {return GEOMETRY_CORNERWEDGE;} + + // Poly Overrides + /*override*/ void buildMesh(); + /*override*/ size_t getFaceFromLegacyNormalId( const NormalId nId ) const; + + }; + +} // namespace diff --git a/App/v8world/Cylinder.h b/App/v8world/Cylinder.h new file mode 100644 index 0000000..abfc005 --- /dev/null +++ b/App/v8world/Cylinder.h @@ -0,0 +1,58 @@ +#pragma once + +#include "V8World/Geometry.h" +#include "V8World/GeometryPool.h" +#include "V8World/BulletGeometryPoolObjects.h" + +namespace RBX { + + class Cylinder: public Geometry + { + public: + typedef GeometryPool BulletCylinderShapePool; + + private: + typedef Geometry Super; + + float realLength, realWidth; + + BulletCylinderShapePool::Token bulletCylinderShape; + + void updateBulletCollisionData(); + + public: + Cylinder(); + ~Cylinder(); + + GeometryType getGeometryType() const override {return GEOMETRY_CYLINDER;} + CollideType getCollideType() const override {return COLLIDE_BULLET;} + + bool setUpBulletCollisionData() override; + + bool hitTest(const RbxRay& rayInMe, Vector3& localHitPoint, Vector3& surfaceNormal) override; + void setSize(const Vector3& _size) override; + + Matrix3 getMoment(float mass) const override; + float getVolume() const override; + + float getRadius() const override; + + Vector3 getCenterToCorner(const Matrix3& rotation) const override; + + size_t closestSurfaceToPoint(const Vector3& pointInBody) const override; + Plane getPlaneFromSurface(const size_t surfaceId) const override; + CoordinateFrame getSurfaceCoordInBody(const size_t surfaceId) const override; + Vector3 getSurfaceNormalInBody(const size_t surfaceId) const override; + size_t getMostAlignedSurface(const Vector3& vecInWorld, const G3D::Matrix3& objectR) const override; + int getNumSurfaces() const override; + Vector3 getSurfaceVertInBody(const size_t surfaceId, const int vertId) const override; + int getNumVertsInSurface(const size_t surfaceId) const override; + bool vertOverlapsFace(const Vector3& pointInBody, const size_t surfaceId) const override; + + bool findTouchingSurfacesConvex(const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId) const override; + bool FacesOverlapped(const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol) const override; + bool FaceVerticesOverlapped(const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol) const override; + bool FaceEdgesOverlapped(const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol) const override; + }; + +} // namespace diff --git a/App/v8world/DistributedPhysics.h b/App/v8world/DistributedPhysics.h new file mode 100644 index 0000000..b4d7f42 --- /dev/null +++ b/App/v8world/DistributedPhysics.h @@ -0,0 +1,17 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +namespace RBX { + namespace Network { + class DistributedPhysics + { + public: + static const float MIN_CLIENT_SIMULATION_DISTANCE() {return 10.0f;} + static const float MAX_CLIENT_SIMULATION_DISTANCE() {return 1000.0f;} + + static const float CLIENT_SLOP() {return 1.05f;} // 105% how far out of the region before client stops simulating + static const float SERVER_SLOP() {return 1.00f;} // server switches simulation to someone else as soon as the object leaves the region + }; + } +} \ No newline at end of file diff --git a/App/v8world/Edge.h b/App/v8world/Edge.h new file mode 100644 index 0000000..9b81e93 --- /dev/null +++ b/App/v8world/Edge.h @@ -0,0 +1,122 @@ + /* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IPipelined.h" +#include "V8World/Enum.h" +#include + +namespace RBX { +namespace Graphics { +class CullableSceneNode; +} } + +namespace RBX { + + class RBXBaseClass Edge : public IPipelined + { + private: + Sim::EdgeState edgeState; + Sim::ThrottleType throttleType; + + Primitive* prim0; + Primitive* prim1; + + int index0; // linked lists for primitive 0 and 1 + int index1; + + protected: + // protected - no one should set it here + virtual void setPrimitive(int i, Primitive* p); + + public: + typedef enum {JOINT, CONTACT} EdgeType; // purely to eliminate dynamic casts + + Edge(Primitive* prim0, Primitive* prim1); + + virtual ~Edge() { + RBXASSERT(index0 == -1); + RBXASSERT(index1 == -1); + RBXASSERT(prim0 == NULL); + RBXASSERT(prim1 == NULL); + index0 = -1; + index1 = -1; + prim0 = static_cast(Debugable::badMemory()); + prim1 = static_cast(Debugable::badMemory()); + } + + // poor man's way of no dynamic casts + virtual EdgeType getEdgeType() const = 0; + + virtual void generateDataForMovingAssemblyStage(void) {} + + Sim::EdgeState getEdgeState() const {return edgeState;} + void setEdgeState(Sim::EdgeState value) {edgeState = value;} + + Sim::ThrottleType getThrottleType() const {return throttleType;} + void setThrottleType(Sim::ThrottleType value) {throttleType = value;} + + template + Type* fastCast(EdgeType edgeType) { + bool match = (this->getEdgeType() == edgeType); + RBXASSERT_VERY_FAST(match == (dynamic_cast(this) != NULL)); + return match ? static_cast(this) : NULL; + } + + Primitive* getPrimitive(int i) { + RBXASSERT_VERY_FAST((i == 0) || (i == 1)); + return (&prim0)[i]; + } + + const Primitive* getConstPrimitive(int i) const { + RBXASSERT_VERY_FAST((i == 0) || (i == 1)); + return (&prim0)[i]; + } + + Primitive* otherPrimitive(const Primitive* p) {return (p == prim0) ? prim1 : prim0;} + RBX::Graphics::CullableSceneNode* otherPrimitive(const RBX::Graphics::CullableSceneNode* p) { RBXASSERT(NULL); return NULL;} + + const Primitive* otherConstPrimitive(const Primitive* p) const {return (p == prim0) ? prim1 : prim0;} + + Primitive* otherPrimitive(int i) { + RBXASSERT_VERY_FAST((i == 0) || (i == 1)); + return (&prim0)[(i + 1) % 2]; + } + + const Primitive* otherConstPrimitive(int i) const { + RBXASSERT_VERY_FAST((i == 0) || (i == 1)); + return (&prim0)[(i + 1) % 2]; + } + + int getPrimitiveId(const Primitive* p) const { + RBXASSERT_VERY_FAST(links(p)); + return (p == prim0) ? 0 : 1; + } + + int getIndex(const Primitive* p) const { + RBXASSERT_VERY_FAST(this->links(p)); + return (p == prim0) ? index0 : index1; + } + + void setIndex(Primitive* p, int index) { + RBXASSERT_VERY_FAST(this->links(p)); + if (p == prim0) { + index0 = index; + } + else { + index1 = index; + } + } + + bool links(const Primitive* p) const { + return ((p == prim0) || (p == prim1)); + } + + bool links(Primitive* p0, Primitive* p1) const { + return ( ((p0 == prim0) && (p1 == prim1)) + || ((p0 == prim1) && (p1 == prim0)) + ); + } + }; + +} // namespace diff --git a/App/v8world/EdgeBuffer.h b/App/v8world/EdgeBuffer.h new file mode 100644 index 0000000..260fee4 --- /dev/null +++ b/App/v8world/EdgeBuffer.h @@ -0,0 +1,46 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" +#include "V8World/Assembly.h" +#include "Util/BiMultiMap.h" + +namespace RBX { + class Assembly; + + class EdgeBuffer : public IWorldStage { + private: + // DEBUG ONLY + typedef RBX::BiMultiMap AssemblyEdgeMap; // find incomplete Joints by primitive + AssemblyEdgeMap assemblyEdges; + + bool debugPushEdgeToDownstream(Edge* e); + bool debugRemoveEdgeFromDownstream(Edge* e); + bool debugAddAssembly(Assembly* a); + bool debugRemoveAssembly(Assembly* a); + + bool assemblyIsHere(Assembly* a); + + void assemblyPrimitiveAdded(Primitive* p); + void assemblyPrimitiveRemoved(Primitive* p); + + void pushEdgeIfOk(Edge* e); + bool pushSpringOk(Edge* e); + bool pushKinematicOk(Edge* e); + void removeEdgeIfDownstream(Edge* e); + + protected: + void afterAssemblyAdded(Assembly* a); + void beforeAssemblyRemoving(Assembly* a); + + EdgeBuffer(IStage* upstream, IStage* downstream, World* world) + : IWorldStage(upstream, downstream, world) + {} + + virtual ~EdgeBuffer(); + + /*override*/ void onEdgeAdded(Edge* e); + /*override*/ void onEdgeRemoving(Edge* e); + }; +} // namespace diff --git a/App/v8world/EdgeStage.h b/App/v8world/EdgeStage.h new file mode 100644 index 0000000..c463ee1 --- /dev/null +++ b/App/v8world/EdgeStage.h @@ -0,0 +1,32 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" + + +namespace RBX { + + class Primitive; + + class EdgeStage : public IWorldStage { + private: + typedef IWorldStage Super; + class ContactStage* getContactStage(); + + public: + /////////////////////////////////////////// + // IStage + EdgeStage(IStage* upstream, World* world); + + ~EdgeStage() {} + + /*override*/ IStage::StageType getStageType() const {return IStage::EDGE_STAGE;} + + /*override*/ void onEdgeAdded(Edge* e); + /*override*/ void onEdgeRemoving(Edge* e); + + void onPrimitiveAdded(Primitive* p); + void onPrimitiveRemoving(Primitive* p); + }; +} // namespace \ No newline at end of file diff --git a/App/v8world/Enum.h b/App/v8world/Enum.h new file mode 100644 index 0000000..2412a7e --- /dev/null +++ b/App/v8world/Enum.h @@ -0,0 +1,54 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +namespace RBX { + + namespace Sim { + + typedef enum { ANCHORED, + RECURSIVE_WAKE_PENDING, + WAKE_PENDING, + AWAKE, + SLEEPING_CHECKING, + SLEEPING_DEEPLY, + REMOVING } AssemblyState; + + inline bool isMovingAssemblyState(AssemblyState state) { + return ((state == AWAKE) || (state == RECURSIVE_WAKE_PENDING) || (state == WAKE_PENDING)); + } + + inline bool isSleepingAssemblyState(AssemblyState state) { + return ((state == SLEEPING_CHECKING) || (state == SLEEPING_DEEPLY)); + } + + inline bool outOfKernelAssemblyState(AssemblyState state) { + return (isSleepingAssemblyState(state) || (state == REMOVING)); + } + +#ifdef _WIN32 + typedef enum : unsigned char { CAN_NOT_THROTTLE = 0, + CAN_THROTTLE, + NUM_THROTTLE_TYPE, + UNDEFINED_THROTTLE } ThrottleType; + + typedef enum : unsigned char { UNDEFINED, + STEPPING, + SLEEPING, + CONTACTING, + CONTACTING_SLEEPING} EdgeState; +#else + typedef enum { CAN_NOT_THROTTLE = 0, + CAN_THROTTLE, + NUM_THROTTLE_TYPE, + UNDEFINED_THROTTLE } ThrottleType; + + typedef enum { UNDEFINED, + STEPPING, + SLEEPING, + CONTACTING, + CONTACTING_SLEEPING} EdgeState; +#endif + + } // namespace WORLD +}// namespace diff --git a/App/v8world/Feature.h b/App/v8world/Feature.h new file mode 100644 index 0000000..a68550d --- /dev/null +++ b/App/v8world/Feature.h @@ -0,0 +1,58 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "Util/G3DCore.h" +#include "rbx/Debug.h" + +namespace RBX { + + class Primitive; + + namespace GEO { + + class RBXBaseClass Feature + { + private: + Primitive* primitive; + int index; + + public: + typedef enum {VERTEX, EDGE, FACE} FeatureType; + + Feature(Primitive* primitive, int index) : primitive(primitive), index(index) + {} + + virtual FeatureType getFeatureType() const = 0; + }; + + class Vertex : public Feature + { + public: + Vertex(Primitive* primitive, int index) : Feature(primitive, index) + {} + + /*override*/ FeatureType getFeatureType() const {return VERTEX;} + }; + + class Edge : public Feature + { + public: + Edge(Primitive* primitive, int index) : Feature(primitive, index) + {} + + /*override*/ FeatureType getFeatureType() const {return EDGE;} + }; + + class Face : public Feature + { + public: + Face(Primitive* primitive, int index) : Feature(primitive, index) + {} + + /*override*/ FeatureType getFeatureType() const {return FACE;} + }; + + } // namespace Feature + +} // namespace \ No newline at end of file diff --git a/App/v8world/Geometry.h b/App/v8world/Geometry.h new file mode 100644 index 0000000..3653dad --- /dev/null +++ b/App/v8world/Geometry.h @@ -0,0 +1,129 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "Util/Units.h" +#include "Util/Face.h" + +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" + +namespace RBX { + + class Geometry + { + private: + Vector3 size; + + protected: + boost::scoped_ptr bulletCollisionObject; + + public: + btCollisionObject* getBulletCollisionObject(void) { return bulletCollisionObject.get(); } + virtual bool setUpBulletCollisionData(void) = 0; + typedef enum { GEOMETRY_UNDEFINED=0, + GEOMETRY_BALL, + GEOMETRY_BLOCK, + GEOMETRY_CYLINDER, + GEOMETRY_WEDGE, + GEOMETRY_PRISM, + GEOMETRY_PYRAMID, + GEOMETRY_PARALLELRAMP, + GEOMETRY_RIGHTANGLERAMP, + GEOMETRY_CORNERWEDGE, + GEOMETRY_MEGACLUSTER, + GEOMETRY_SMOOTHCLUSTER, + GEOMETRY_TRI_MESH } GeometryType; + + typedef enum { COLLIDE_BALL=1, + COLLIDE_BLOCK, + COLLIDE_POLY, + COLLIDE_BULLET } CollideType; + + Geometry() : bulletCollisionObject(NULL) + {} + + virtual ~Geometry() + {} + + virtual GeometryType getGeometryType() const = 0; + + virtual CollideType getCollideType() const = 0; + + /////////////////////////////////////////// + // Size and Extents + // + + // Grid Size + virtual void setSize(const G3D::Vector3& _size) { + size = _size; + } + const G3D::Vector3& getSize() const {return size;} + + // Parameters + virtual void setGeometryParameter(const std::string& parameter, int value) { + RBXASSERT(0); // stock geometry does not handle parameters. + } + virtual int getGeometryParameter(const std::string& parameter) const { + RBXASSERT(0); // stock geometry does not handle parameters. + return 0; + } + + // Radius + virtual float getRadius() const = 0; + + // Dragger support + virtual size_t closestSurfaceToPoint( const Vector3& pointInBody ) const = 0; + virtual Plane getPlaneFromSurface( const size_t surfaceId ) const = 0; + virtual CoordinateFrame getSurfaceCoordInBody( const size_t surfaceId ) const = 0; + virtual Vector3 getSurfaceNormalInBody( const size_t surfaceId ) const = 0; + virtual size_t getMostAlignedSurface( const Vector3& vecInWorld, const G3D::Matrix3& objectR ) const = 0; + virtual int getNumSurfaces( void ) const = 0; + virtual Vector3 getSurfaceVertInBody( const size_t surfaceId, const int vertId ) const = 0; + virtual int getNumVertsInSurface( const size_t surfaceId ) const = 0; + virtual bool vertOverlapsFace( const Vector3& pointInBody, const size_t surfaceId ) const = 0; + virtual size_t getFaceFromLegacyNormalId( const NormalId nId ) const { return nId; } + virtual bool isGeometryOrthogonal( void ) const { return true; } + + // Relative proximity + /*override*/virtual bool findTouchingSurfacesConvex( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId ) const = 0; + /*override*/virtual bool FacesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const = 0; + /*override*/virtual bool FaceVerticesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const = 0; + /*override*/virtual bool FaceEdgesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const = 0; + + // Corner (better than radius for big blocks) + virtual Vector3 getCenterToCorner(const Matrix3& rotation) const {return Vector3::zero();} + + // CofmOffset + virtual Vector3 getCofmOffset() const {return Vector3::zero();} + + // Moment + virtual Matrix3 getMoment(float mass) const {return Matrix3::zero();} + + // Volume + virtual float getVolume() const {return size.x * size.y * size.z;} + + // Hit Test + virtual bool hitTest(const RbxRay& rayInMe, Vector3& localHitPoint, Vector3& surfaceNormal) {return false;} + + virtual bool collidesWithGroundPlane(const CoordinateFrame& c, float yHeight) const { + return ((c.translation.y - getRadius()) < yHeight); + } + + virtual std::vector polygonIntersectionWithFace( const std::vector& polygonInBody, const size_t surfaceId ) const { + std::vector empty; + return empty; + } + + // Cluster RTTI helper + bool isTerrain() const + { + GeometryType type = getGeometryType(); + + return type == GEOMETRY_MEGACLUSTER || type == GEOMETRY_SMOOTHCLUSTER; + } + + // Cluster dragger helper + virtual bool hitTestTerrain(const RbxRay& rayInMe, Vector3& localHitPoint, int& surfId, CoordinateFrame& surfCf) { return false; } + }; + +} // namespace diff --git a/App/v8world/GeometryPool.h b/App/v8world/GeometryPool.h new file mode 100644 index 0000000..ca5dec5 --- /dev/null +++ b/App/v8world/GeometryPool.h @@ -0,0 +1,231 @@ +#pragma once +#include "rbx/threadsafe.h" +#include "rbx/debug.h" +#include +#include +#include + +namespace RBX { + + struct Vector3Comparer { + bool operator()(const Vector3& a, const Vector3& b) const { + if (a.xb.x) return false; + if (a.yb.y) return false; + if (a.z()(v.x); + boost::hash_combine(result, v.y); + boost::hash_combine(result, v.z); + return result; + } + }; + + struct Vector3_2Ints + { + Vector3 vectPart; + int int1; + int int2; + bool operator==(const Vector3_2Ints& b) const + { + return vectPart==b.vectPart && int1==b.int1 && int2==b.int2; + } + }; + + struct Vector3_2IntsComparer { + bool operator()(const Vector3_2Ints& a, const Vector3_2Ints& b) const { + if (a.vectPart.xb.vectPart.x) return false; + if (a.vectPart.yb.vectPart.y) return false; + if (a.vectPart.zb.vectPart.z) return false; + if (a.int1b.int1) return false; + if (a.int2 + class GeometryPool + { + private: + struct Entry; + typedef std::map Map; + + struct Entry + { + Value value; + size_t count; + typename Map::iterator iterator; + + Entry(const Key& key) + : value(key) + , count(0) + { + } + }; + + class StaticData + { + public: + Map map; + rbx::spin_mutex mutex; + }; + + SAFE_STATIC(StaticData, staticData); + + static StaticData& getStaticData() + { + return staticData(); + } + + public: + // This is modeled after unique_ptr with custom deleter GeometryPool::returnToken + class Token + { + Token(const Token& token); + Token& operator=(const Token& token); + + public: + Token(): entry(0) + { + } + + explicit Token(Entry* entry) + : entry(entry) + { + } + + Token(Token&& other) + : entry(other.entry) + { + other.entry = 0; + } + + ~Token() + { + if (entry) + GeometryPool::returnToken(entry); + } + + Token& operator=(Token&& other) + { + if (entry) + GeometryPool::returnToken(entry); + + entry = other.entry; + other.entry = 0; + + return *this; + } + + const Value& operator*() const + { + return entry->value; + } + + const Value* operator->() const + { + return &entry->value; + } + + // This is slightly horrible but we don't have C++11 explicit operator bool + operator void*() const + { + return entry; + } + + private: + Entry* entry; + }; + + static void init() { staticData(); } + + static Token getToken(const Key& key, const Key& data) + { + StaticData &d = getStaticData(); + rbx::spin_mutex::scoped_lock lock(d.mutex); + + typename Map::iterator it = d.map.find(key); + Entry* entry = (it != d.map.end()) ? it->second : 0; + + if (!entry) + { + entry = new Entry(data); + entry->iterator = d.map.insert(typename Map::value_type(key, entry)).first; + } + + entry->count++; + + return Token(entry); + } + + static Token getToken(const Key& key) + { + return getToken(key, key); + } + + static void returnToken(Entry* entry) + { + StaticData &d = getStaticData(); + rbx::spin_mutex::scoped_lock lock(d.mutex); + + RBXASSERT(entry->count > 0); + entry->count--; + + if (entry->count == 0) + { + RBXASSERT(entry == entry->iterator->second); + + d.map.erase(entry->iterator); + delete entry; + } + } + + static int getSize() + { + StaticData &d = getStaticData(); + rbx::spin_mutex::scoped_lock lock(d.mutex); + + return d.map.size(); + } + }; + +} // namespace RBX diff --git a/App/v8world/GlueJoint.h b/App/v8world/GlueJoint.h new file mode 100644 index 0000000..c82e494 --- /dev/null +++ b/App/v8world/GlueJoint.h @@ -0,0 +1,89 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/MultiJoint.h" +#include + +namespace RBX { + class Constraint; + class GlueJoint : public MultiJoint + { + private: + typedef MultiJoint Super; + Face faceInJointSpace; + + // Used when PGS is on + std::vector< Constraint* > constraints; + + float getMaxForce(); + + // Joint + /*override*/ JointType getJointType() const {return Joint::GLUE_JOINT;} + /*override*/ bool isBreakable() const {return true;} + /*override*/ bool isBroken() const; + + static bool compatibleSurfaces( + Primitive* p0, + Primitive* p1, + NormalId nId0, + NormalId nId1); + protected: + // Edge + /*override*/ void putInKernel(Kernel* kernel); + /*override*/ void removeFromKernel(); + public: + GlueJoint(); + + GlueJoint( + Primitive* p0, + Primitive* p1, + const CoordinateFrame& jointCoord0, + const CoordinateFrame& jointCoord1, + const Face& faceInJointSpace); + + const Vector3& getFacePoint(int i) const { // in joint space (common to both P0 and P1) + RBXASSERT(i >= 0 && i < 4); + return faceInJointSpace[i]; + } + + void setFacePoint(int i, const Vector3& value) { // in joint space + RBXASSERT(i >= 0 && i < 4); + faceInJointSpace[i] = value; + } + + static GlueJoint* canBuildJoint( + Primitive* p0, + Primitive* p1, + NormalId nId0, + NormalId nId1); + + }; + + class ManualGlueJoint : public GlueJoint + { + private: + typedef GlueJoint Super; + size_t surface0; // surface from primitive 0 + size_t surface1; // surface from primitive 1 + + /*override*/ virtual JointType getJointType() const {return MANUAL_GLUE_JOINT;} + /*override*/ void putInKernel(Kernel* kernel); + /*override*/ void computeIntersectingSurfacePoints(void); + + public: + ManualGlueJoint() {surface0 = (size_t)-1; surface1 = (size_t)-1;} + ManualGlueJoint(size_t s0, size_t s1, Primitive* prim0, Primitive* prim1, const CoordinateFrame& c0, const CoordinateFrame &c1, const Face& faceInJointSpace) + : GlueJoint(prim0, prim1, c0, c1, faceInJointSpace) + {surface0 = s0; surface1 = s1;} + + ~ManualGlueJoint() {} + + size_t getSurface0(void) const {return surface0;} + size_t getSurface1(void) const {return surface1;} + void setSurface0(size_t surfId) {surface0 = surfId;} + void setSurface1(size_t surfId) {surface1 = surfId;} + }; + + +} // namespace diff --git a/App/v8world/GroundStage.h b/App/v8world/GroundStage.h new file mode 100644 index 0000000..a5b8917 --- /dev/null +++ b/App/v8world/GroundStage.h @@ -0,0 +1,53 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" + +namespace RBX { + + class Primitive; + class Joint; + class KernelJoint; + class RigidJoint; + + class GroundStage : public IWorldStage { + private: + typedef IWorldStage Super; + class EdgeStage* getEdgeStage(); + + bool kernelJointHere(Primitive* p); + + void addGroundJoint(Primitive* p, bool grounded); + void removeGroundJoint(Primitive* p, bool grounded); + + void onKernelJointAdded(KernelJoint* k); + void onKernelJointRemoving(KernelJoint* k); + + void checkForFreeGroundJoint(RigidJoint* r); + void rebuildFreeGround(Primitive* p); + void rebuildOthers(Primitive* changedP); + + RigidJoint* heaviestRigidToGround(Primitive* p); + + public: + /////////////////////////////////////////// + // IStage + GroundStage(IStage* upstream, World* world); + ~GroundStage(); + + /*override*/ IStage::StageType getStageType() const {return IStage::GROUND_STAGE;} + + void onPrimitiveAdded(Primitive* p); + void onPrimitiveRemoving(Primitive* p); + + void onPrimitiveFixedChanging(Primitive* p); + void onPrimitiveFixedChanged(Primitive* p); + + /*override*/ void onEdgeAdded(Edge* e); + /*override*/ void onEdgeRemoving(Edge* e); + }; + + +} // namespace + diff --git a/App/v8world/HumanoidStage.h b/App/v8world/HumanoidStage.h new file mode 100644 index 0000000..f2cfe1b --- /dev/null +++ b/App/v8world/HumanoidStage.h @@ -0,0 +1,35 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" + +namespace RBX { + + class Assembly; + + class HumanoidStage : public IWorldStage + { + private: + std::set movingHumanoidAssemblies; + void toDynamics(Assembly* a); + void toHumanoid(Assembly* a); + void fromDynamics(Assembly* a); + void fromHumanoid(Assembly* a); + + + public: + HumanoidStage(IStage* upstream, World* world); + + ~HumanoidStage(); + + /*override*/ IStage::StageType getStageType() const {return IStage::HUMANOID_STAGE;} + + void onAssemblyAdded(Assembly* assembly); + void onAssemblyRemoving(Assembly* assembly); + + const std::set& getMovingHumanoidAssemblies() { + return movingHumanoidAssemblies; + } + }; +} // namespace diff --git a/App/v8world/IMoving.h b/App/v8world/IMoving.h new file mode 100644 index 0000000..91b05ed --- /dev/null +++ b/App/v8world/IMoving.h @@ -0,0 +1,97 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "rbx/Debug.h" +#include +#include "rbx/Boost.hpp" +#include "rbx/rbxTime.h" + +namespace RBX { + + class IMovingManager; + class MovementHistory; + class Velocity; + class Primitive; + + class RBXBaseClass IMoving + { + friend class IMovingManager; + + private: + IMovingManager* iMovingManager; + + int stepsToSleep; + + scoped_ptr lastCFrame; + Time lastUpdateTime; + scoped_ptr movementHistory; + + void makeMoving(); + + protected: + virtual void onSleepingChanged(bool sleeping) = 0; + + void setMovingManager(IMovingManager* _iMovingManager); + + bool checkSleep(); + + public: + IMoving(); + + ~IMoving(); + + void notifyMoved(); // done in PartInstance::setCoordinateFrame, InterpolatedCFrame, and by the World after every step + + virtual bool reportTouches() const = 0; + + virtual void onClumpChanged() = 0; // callback to PartInstance from Primitive + + virtual void onNetworkIsSleepingChanged(Time now) = 0; // callback to PartInstance from Primitive + + virtual void onBuoyancyChanged( bool value ) = 0; // callback to PartInstance from Primitive + virtual bool isInContinousMotion() = 0; + + virtual const Primitive* getConstPartPrimitiveVirtual() const {return NULL;} + + bool getSleeping() const { + return (stepsToSleep == 0); + } + + void forceSleep(); + + const MovementHistory& getMovementHistory() const; + void clearMovementHistory(); + void addMovementNode(const CoordinateFrame& cFrame, const Velocity& velocity, const Time& timeStamp); + void setLastCFrame(const CoordinateFrame& cFrame); + const CoordinateFrame& getLastCFrame(const CoordinateFrame& defaultCFrame) const; + bool hasLastCFrame() {return lastCFrame != NULL;} + void setLastUpdateTime(const Time& time); + const Time& getLastUpdateTime() const; + }; + + class RBXBaseClass IMovingManager + { + friend class IMoving; + private: + typedef std::set MovingSet; + MovingSet moving; + MovingSet::iterator current; + + protected: + void remove(IMoving* iMoving); + void moved(IMoving* iMoving); + + public: + IMovingManager(); + + virtual ~IMovingManager(); + + void onMovingHeartbeat(); // put parts to sleep here if not moving for a long time, notify + + int getNumberMoving() const {return static_cast(moving.size());} + + void updateHistory(); + }; + +} // namespace \ No newline at end of file diff --git a/App/v8world/IPipelined.h b/App/v8world/IPipelined.h new file mode 100644 index 0000000..9f0af1d --- /dev/null +++ b/App/v8world/IPipelined.h @@ -0,0 +1,88 @@ + /* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" +#include "rbx/Debug.h" + +namespace RBX { + + class Kernel; + + class RBXBaseClass IPipelined + { + private: + IStage* currentStage; + + void removeFromStage(IStage::StageType stageType); + + IStage* getStage(IStage::StageType stageType) const; + public: + IPipelined() : currentStage(NULL) + {} + + virtual ~IPipelined() { + RBXASSERT(currentStage == NULL); + currentStage = static_cast(Debugable::badMemory()); + } + + void putInPipeline(IStage* stage); + + void removeFromPipeline(IStage* stage); + + void putInStage(IStage* stage); + + void removeFromStage(IStage* stage); + + bool inPipeline() const { + return (currentStage != NULL); + } + + const IStage* getCurrentStage() const {return currentStage;} + + bool inStage(IStage::StageType stageType) const { + RBXASSERT(currentStage); + return (currentStage && (currentStage->getStageType() == stageType)); + } + + bool inStage(IStage* iStage) const { + RBXASSERT(iStage); + RBXASSERT(currentStage); + return (currentStage == iStage); + } + + bool inOrDownstreamOfStage(IStage::StageType stageType) const { + RBXASSERT(currentStage); + return (currentStage && (currentStage->getStageType() >= stageType)); + } + + bool inOrDownstreamOfStage(IStage* iStage) const { + RBXASSERT(iStage); + RBXASSERT(currentStage); + return (currentStage && iStage && (currentStage->getStageType() >= iStage->getStageType())); + } + + bool downstreamOfStage(IStage* iStage) const { + RBXASSERT(iStage); + RBXASSERT(currentStage); + return (currentStage && iStage && currentStage->getStageType() > iStage->getStageType()); + } + + bool inKernel() const {return inStage(IStage::KERNEL_STAGE);} + Kernel* getKernel() const; // should never fail + + virtual void putInKernel(Kernel* kernel); + virtual void removeFromKernel(); + + World* findWorld() { + if (!currentStage) { + return NULL; + } + else { + IStage* worldStage = (!inKernel()) ? currentStage : currentStage->getUpstream(); + return rbx_static_cast(worldStage)->getWorld(); + } + } + }; + +} // namespace diff --git a/App/v8world/IWorldStage.h b/App/v8world/IWorldStage.h new file mode 100644 index 0000000..d508a82 --- /dev/null +++ b/App/v8world/IWorldStage.h @@ -0,0 +1,48 @@ + /* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Kernel/IStage.h" +#include "rbx/Debug.h" +#include "Util/G3DCore.h" + +namespace RBX { + + class World; + class Edge; + class Contact; + class Primitive; + + class RBXBaseClass IWorldStage : public IStage { + private: + World* world; + + public: + typedef enum { NUM_CONTACTSTAGE_CONTACTS, + NUM_STEPPING_CONTACTS, + NUM_TOUCHING_CONTACTS, + MAX_TREE_DEPTH } MetricType; + + IWorldStage(IStage* upstream, IStage* downstream, World* world) + : IStage(upstream, downstream) + , world(world) + {} + + IWorldStage* getUpstreamWS() {return rbx_static_cast(getUpstream());} + IWorldStage* getDownstreamWS() {return rbx_static_cast(getDownstream());} + const IWorldStage* getDownstreamWS() const {return rbx_static_cast(getDownstream());} + + World* getWorld() {return world;} + + //////////////////////////////////////////// + // + // Calls to DOWNSTREAM stage + virtual void onEdgeAdded(Edge* e); + virtual void onEdgeRemoving(Edge* e); + + virtual int getMetric(MetricType metricType) { + RBXASSERT(getDownstreamWS()); + return getDownstreamWS()->getMetric(metricType); + } + }; +} // namespace \ No newline at end of file diff --git a/App/v8world/Joint.h b/App/v8world/Joint.h new file mode 100644 index 0000000..b2a972c --- /dev/null +++ b/App/v8world/Joint.h @@ -0,0 +1,243 @@ +#pragma once + +#include "V8World/Edge.h" +#include "Util/SurfaceType.h" +#include "Util/Face.h" +#include "Util/Extents.h" +#include "Util/SpanningEdge.h" +#include "G3D/Array.h" +#include "boost/intrusive/list.hpp" + + +namespace RBX { + + class Channel; + class Link; + class Joint; + + class RBXInterface IJointOwner + { + public: + virtual Joint* getJoint(void) { RBXASSERT(0); return NULL; } + }; + + class StepJointsStage; + typedef boost::intrusive::list_base_hook< boost::intrusive::tag > StepJointsStageHook; + class MovingAssemblyStage; + typedef boost::intrusive::list_base_hook< boost::intrusive::tag > MovingAssemblyStageHook; + + class Joint : public Edge + , public SpanningEdge + , public StepJointsStageHook + , public MovingAssemblyStageHook // TODO: Can we share the same hooks? + { + private: + typedef Edge Super; + IJointOwner* jointOwner; + + static bool canBuildJoint( + Primitive* p0, + Primitive* p1, + NormalId nId0, + NormalId nId1, + float angleMax, + float planarMax); + + protected: + CoordinateFrame jointCoord0; // in object space + CoordinateFrame jointCoord1; // this coord is aligned with Coord0, so it points into the body + public: + static bool canBuildJointLoose(Primitive* p0, Primitive* p1, NormalId nId0, NormalId nId1); + static bool canBuildJointTight(Primitive* p0, Primitive* p1, NormalId nId0, NormalId nId1); + +protected: + /////////////////////////////////////////////////// + // Edge + // + /*override*/ EdgeType getEdgeType() const {return Edge::JOINT;} + + Joint(); + + Joint( Primitive* prim0, + Primitive* prim1, + const CoordinateFrame& _jointCoord0, + const CoordinateFrame& _jointCoord1); + + public: + ~Joint(); + + void setJointCoord(int i, const CoordinateFrame& c); + + const CoordinateFrame& getJointCoord(int i) const { + return (i == 0) ? jointCoord0 : jointCoord1; + } + + CoordinateFrame getJointWorldCoord(int i); + + void notifyMoved(); + + // In precedence order - greatest to least + // GROUND KINEMATIC SPRING KERNEL + typedef enum { ANCHOR_JOINT, // X + WELD_JOINT, // X + MANUAL_WELD_JOINT, // X + SNAP_JOINT, // X + MOTOR_1D_JOINT, // X + MOTOR_6D_JOINT, // X + ROTATE_JOINT, // X + ROTATE_P_JOINT, // X + ROTATE_V_JOINT, // X + GLUE_JOINT, // X + MANUAL_GLUE_JOINT, // X + FREE_JOINT, // X + KERNEL_JOINT, // X + NO_JOINT // + } JointType; + + ////////////////////////////////////////////////// + // IJointOwner + // + void setJointOwner(IJointOwner* value); + IJointOwner* getJointOwner() const; + + /////////////////////////////////////////////////// + // Edge Virtuals + // + /*override*/ void setPrimitive(int i, Primitive* p); + + /////////////////////////////////////////////////// + // Joint Virtuals + // + virtual JointType getJointType() const {RBXASSERT(0); return Joint::NO_JOINT;} + virtual bool isBreakable() const {return false;} + virtual bool isBroken() const {return false;} + virtual bool joinsFace(Primitive* g, NormalId faceId) const {return false;} + virtual bool isAligned() {return true;} + virtual CoordinateFrame align(Primitive* pMove, Primitive* pStay) {RBXASSERT(0); return CoordinateFrame();} + + virtual void setPhysics() {} // occurs after networking read; + + virtual bool canStepWorld() const {return false;} + virtual bool canStepUi() const {return false;} + + virtual void stepWorld() {} + virtual bool stepUi(double distributedGameTime) {return false;} + + ////////////////////////////////////////////////////////// + // + static bool isJoint(const Edge* e) {return (e->getEdgeType() == Edge::JOINT);} + + static JointType getJointType(const Edge* e) { + return isJoint(e) ? rbx_static_cast(e)->getJointType() : Joint::NO_JOINT; + } + + static bool isGroundJoint(const Edge* e) { // alternately, created by AutoJoin + Joint::JointType jt = getJointType(e); + return ((jt == FREE_JOINT) || (jt == ANCHOR_JOINT)); + } + + static bool isRigidJoint(const Edge* e) { + Joint::JointType jt = getJointType(e); + return ((jt == WELD_JOINT) || (jt == SNAP_JOINT) || (jt == MANUAL_WELD_JOINT)); + } + + static bool isKinematicJoint(const Edge* e) { + Joint::JointType jt = getJointType(e); + return ((jt >= WELD_JOINT) && (jt <= MOTOR_6D_JOINT)); + } + + static bool isSpringJoint(const Edge* e) { + Joint::JointType jt = getJointType(e); + return ((jt >= ROTATE_JOINT) && (jt <= GLUE_JOINT)) || (jt == MANUAL_GLUE_JOINT); + } + + static bool isMotorJoint(const Edge* e) { + Joint::JointType jt = getJointType(e); + return ((jt >= MOTOR_1D_JOINT) && (jt <= MOTOR_6D_JOINT)); + } + + static bool isKernelJoint(const Edge* e) { + Joint::JointType jt = getJointType(e); + return (jt == Joint::KERNEL_JOINT); + } + + static bool isManualJoint(const Edge* e) { + Joint::JointType jt = getJointType(e); + return (jt == Joint::MANUAL_WELD_JOINT || jt == Joint::MANUAL_GLUE_JOINT); + } + + static bool isSpanningTreeJoint(const Edge* e) { + return (isKinematicJoint(e) || isSpringJoint(e) || isGroundJoint(e)); + } + + static bool isAutoJoint(const Joint* j) { // alternately, created by AutoJoin + return (!isGroundJoint(j) && !isKernelJoint(j) && !isManualJoint(j)); + } + + static Joint* getJoint(Primitive* p, Joint::JointType jointType); + static const Joint* getConstJoint(const Primitive* p, Joint::JointType jointType); + static const Joint* findConstJoint(const Primitive* p, Joint::JointType jointType); + + NormalId getNormalId(int i) const { + RBXASSERT((i==0)||(i==1)); + return (i == 0) + ? Matrix3ToNormalId(jointCoord0.rotation) + : normalIdOpposite(Matrix3ToNormalId(jointCoord1.rotation)); + } + + virtual Link* resetLink() { RBXASSERT(!"Not Implemented"); return 0; } + + static bool FacesOverlapped( const Primitive* p0, size_t face0Id, const Primitive* p1, size_t face1Id, float adjustPartTolerance = 1.0 ); + static bool FaceVerticesOverlapped( const Primitive* p0, size_t face0Id, const Primitive* p1, size_t face1Id, float adjustPartTolerance ); + static bool FaceEdgesOverlapped( const Primitive* p0, size_t face0Id, const Primitive* p1, size_t face1Id, float adjustPartTolerance ); + static bool findTouchingSurfacesConvex( const Primitive& p0, size_t& face0Id, const Primitive& p1, size_t& face1Id ); + static bool compatibleForGlueAutoJoint( const Primitive& p0, size_t& face0Id, const Primitive& p1, size_t& face1Id ); + static bool compatibleForWeldAutoJoint( const Primitive& p0, size_t& face0Id, const Primitive& p1, size_t& face1Id ); + static bool compatibleForHingeAutoJoint( const Primitive& p0, size_t& face0Id, const Primitive& p1, size_t& face1Id ); + static bool compatibleForStudAutoJoint( const Primitive& p0, size_t& face0Id, const Primitive& p1, size_t& face1Id ); + static bool inCompatibleForAnyJoint( const Primitive& p0, size_t& face0Id, const Primitive& p1, size_t& face1Id ); + static bool positionedForStudAutoJoint( const Primitive& p0, size_t& face0Id, const Primitive& p1, size_t& face1Id ); + static SurfaceType getSurfaceTypeFromNormal( const Primitive& primitive, const NormalId& normalId ); // helper function for correct "isCompatible" function behavior + + ///////////////////////////////////////////////////////////////// + // SpanningEdge + private: + /*override*/ bool isHeavierThan(const SpanningEdge* other) const; + /*override*/ SpanningNode* otherNode(SpanningNode* n); + /*override*/ const SpanningNode* otherConstNode(const SpanningNode* n) const; + /*override*/ SpanningNode* getNode(int i); + /*override*/ const SpanningNode* getConstNode(int i) const; + }; + + + + class AnchorJoint : public Joint + { + private: + /*override*/ virtual JointType getJointType() const {return Joint::ANCHOR_JOINT;} + + public: + AnchorJoint(Primitive* prim) : Joint(prim, NULL, CoordinateFrame(), CoordinateFrame()) + {} + + static bool isAnchorJoint(const Joint* j) { + return (j->getJointType() == Joint::ANCHOR_JOINT); + } + }; + + class FreeJoint : public Joint + { + private: + /*override*/ virtual JointType getJointType() const {return Joint::FREE_JOINT;} + + public: + FreeJoint(Primitive* prim) : Joint(prim, NULL, CoordinateFrame(), CoordinateFrame()) + {} + + static bool isFreeJoint(const Joint* j) { + return (j->getJointType() == Joint::FREE_JOINT); + } + }; + +} // namespace + diff --git a/App/v8world/JointBuilder.h b/App/v8world/JointBuilder.h new file mode 100644 index 0000000..0d05ccc --- /dev/null +++ b/App/v8world/JointBuilder.h @@ -0,0 +1,19 @@ +#pragma once + +// #include "V8World/Joint.h" + +namespace RBX { + + class Joint; + class Primitive; + + class JointBuilder + { + public: +// static Joint* makeJoint(Primitive* p0, Primitive* p1, const CoordinateFrame& c0, const CoordinateFrame& c1, Joint::JointType jointType); + + static Joint* canJoin(Primitive* p0, Primitive* p1); + }; + +} // namespace + diff --git a/App/v8world/JointStage.h b/App/v8world/JointStage.h new file mode 100644 index 0000000..3a27018 --- /dev/null +++ b/App/v8world/JointStage.h @@ -0,0 +1,52 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" +#include "Util/ConcurrencyValidator.h" +#include "Util/BiMultiMap.h" + +namespace RBX { + + class Edge; + class Joint; + + class JointStage : public IWorldStage { + private: + ConcurrencyValidator concurrencyValidator; + class GroundStage* getGroundStage(); + + typedef RBX::BiMultiMap JointMap; // find incomplete Joints by primitive + JointMap jointMap; // is identical to the primitive fields stored in the fields + std::set incompleteJoints; + std::set primitivesHere; + // of all joints in the incompleteJoints list + void moveEdgeToDownstream(Edge* e); + void removeEdgeFromDownstream(Edge* e); + + void moveJointToDownstream(Joint* j); + void removeJointFromDownstream(Joint* j); + + void putJointHere(Joint* j); + void removeJointFromHere(Joint* j); + + bool edgeHasPrimitiveHere(Edge *e, Primitive* p); + bool edgeHasPrimitivesHere(Edge *e); + void visitAddedPrimitive(Primitive* p, Joint* j, std::vector& jointsToPush); + + public: + /////////////////////////////////////////// + // IStage + JointStage(IStage* upstream, World* world); + + ~JointStage(); + + /*override*/ IStage::StageType getStageType() const {return IStage::JOINT_STAGE;} + + /*override*/ void onEdgeAdded(Edge* e); + /*override*/ void onEdgeRemoving(Edge* e); + + void onPrimitiveAdded(Primitive* p); + void onPrimitiveRemoving(Primitive* p); + }; +} // namespace \ No newline at end of file diff --git a/App/v8world/KDTree.h b/App/v8world/KDTree.h new file mode 100644 index 0000000..aa2c89d --- /dev/null +++ b/App/v8world/KDTree.h @@ -0,0 +1,82 @@ +#pragma once + +#include "util/G3DCore.h" + +class btTriangleCallback; + +namespace RBX { + +union KDNode +{ + struct + { + // left child contents is to the left of splits[0] along axis + // right child contents is to the right of splits[1] along axis + float splits[2]; + + // axis index; must be 0/1/2 (X/Y/Z) + unsigned int axis: 2; + + // left child is at childIndex; right child is at childIndex+1 + unsigned int childIndex: 30; + } branch; + + struct + { + unsigned int triangles[2]; + + // axis index; must be 3 so that isLeaf() can distinguish nodes + unsigned int axis: 2; + + unsigned int triangleCount: 30; + } leaf; + + bool isLeaf() const + { + return branch.axis == 3; + } +}; + +struct KDTree +{ + const Vector3* vertexPositions; + const unsigned char* vertexMaterials; + const unsigned int* indices; + + std::vector nodes; + size_t depth; + Vector3 extentsMin; + Vector3 extentsMax; + + struct RayResult + { + float fraction; + const KDTree* tree; + unsigned int triangle; + + RayResult(): fraction(1), tree(NULL), triangle(0) + { + } + + RayResult(float fraction, const KDTree* tree, unsigned int triangle): fraction(fraction), tree(tree), triangle(triangle) + { + } + + bool hasHit() const + { + return tree != 0; + } + }; + + KDTree(); + + void build(const Vector3* vertexPositions, const unsigned char* vertexMaterials, size_t vertexCount, const unsigned int* indices, size_t triangleCount); + + void queryAABB(btTriangleCallback* callback, const Vector3& aabbMin, const Vector3& aabbMax) const; + void queryRay(RayResult& result, const Vector3& raySource, const Vector3& rayTarget) const; + + Vector3 getTriangleNormal(unsigned int triangle) const; + unsigned char getMaterial(unsigned int triangle, const Vector3& position) const; +}; + +} \ No newline at end of file diff --git a/App/v8world/KernelJoint.h b/App/v8world/KernelJoint.h new file mode 100644 index 0000000..4fa52aa --- /dev/null +++ b/App/v8world/KernelJoint.h @@ -0,0 +1,43 @@ +#pragma once + +#include "V8World/Joint.h" +#include "V8Kernel/Connector.h" + +namespace RBX { + + class KernelJoint + : public Joint + , public Connector // Implements "computeForce" + { + private: + typedef Joint Super; + // IPipelined + protected: + /*override*/ void putInKernel(Kernel* kernel); + /*override*/ void removeFromKernel(); + private: + // Joint + /*override*/ JointType getJointType() const {return Joint::KERNEL_JOINT;} + + // Connector + /*override*/ Body* getBody(BodyIndex id) { + RBXASSERT(inKernel()); + if (id == body0) { + return getEngineBody(); + } + else { + return NULL; + } + } + + protected: + /*implement*/ virtual Body* getEngineBody() = 0; + /*override*/ KernelType getConnectorKernelType() const {return Connector::KERNEL_JOINT;} + + public: + KernelJoint() {} + ~KernelJoint() {} + }; + +} // namespace + diff --git a/App/v8world/MaterialProperties.h b/App/v8world/MaterialProperties.h new file mode 100644 index 0000000..a4b4852 --- /dev/null +++ b/App/v8world/MaterialProperties.h @@ -0,0 +1,43 @@ +#pragma once +#include "util/PartMaterial.h" + + +DYNAMIC_FASTFLAG(MaterialPropertiesEnabled) + +namespace RBX { + +class PhysicalProperties; +class ContactParams; +class Primitive; + +class MaterialProperties +{ +private: + // Helpers + static float calculateUsingWeightedAverage(float weightA, float coeffA, float weightB, float coeffB); + // Used on PartInstance initialization and property setting + static float getDefaultMaterialFriction(PartMaterial material); + static float getDefaultMaterialFrictionWeight(PartMaterial material); + static float getDefaultMaterialElasticity(PartMaterial material); + static float getDefaultMaterialElasticityWeight(PartMaterial material); + static float getDefaultMaterialDensity(PartMaterial material); +public: + // Physical Behavior functions + // Update Contact Parameters between two primitives, and Primitive to Terrain + static void updateContactParamsPrims(ContactParams& params, Primitive* prim0, Primitive* prim1); + static void updateContactParamsPrimMaterial(ContactParams& params, Primitive* prim, Primitive* otherPrim, PartMaterial otherMaterial); + + static float getDensity(Primitive* prim); + + // For humanoid Behavior + static float frictionBetweenMaterials(PartMaterial materialA, PartMaterial materialB); + static float frictionBetweenPrimAndMaterial(Primitive* primA, PartMaterial materialB); + + // Property defaults helper + static PhysicalProperties generatePhysicalMaterialFromPartMaterial(PartMaterial material); + static PhysicalProperties getPrimitivePhysicalProperties(Primitive* prim); + +}; + + +} // NAMESPACE RBX \ No newline at end of file diff --git a/App/v8world/MechToAssemblyStage.h b/App/v8world/MechToAssemblyStage.h new file mode 100644 index 0000000..4081900 --- /dev/null +++ b/App/v8world/MechToAssemblyStage.h @@ -0,0 +1,33 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" + +namespace RBX { + class Assembly; + class Mechanism; + class AssemblyStage; + + class MechToAssemblyStage : public IWorldStage + { + private: + AssemblyStage* getAssemblyStage(); + + public: + MechToAssemblyStage(IStage* upstream, World* world); + + ~MechToAssemblyStage(); + + /*override*/ IStage::StageType getStageType() const {return IStage::MECH_TO_ASSEMBLY_STAGE;} + + void onFixedAssemblyAdded(Assembly* a); + void onFixedAssemblyRemoving(Assembly* a); + + void onSimulateAssemblyRootAdded(Assembly* a); + void onSimulateAssemblyRootRemoving(Assembly* a); + + void onNoSimulateAssemblyRootAdded(Assembly* a); + void onNoSimulateAssemblyRootRemoving(Assembly* a); + }; +} // namespace diff --git a/App/v8world/Mechanism.h b/App/v8world/Mechanism.h new file mode 100644 index 0000000..184139e --- /dev/null +++ b/App/v8world/Mechanism.h @@ -0,0 +1,73 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IPipelined.h" +#include "Util/IndexedMesh.h" +#include "boost/utility.hpp" +#include "Assembly.h" + +namespace RBX { + class Primitive; + + class Mechanism + : public IPipelined + , public boost::noncopyable + , public IndexedMesh + { + private: + static bool assemblyHasMovingParent(const Assembly* a); + + public: + Mechanism(); + + ~Mechanism(); + + Primitive* getMechanismPrimitive(); + const Primitive* getConstMechanismPrimitive() const; + + Assembly* getRootAssembly(); + const Assembly* getConstRootAssembly() const; + + /////////////////////////////////////////////////////////////// + // Primitive Stuff + + static bool isMechanismRootPrimitive(const Primitive* p); + + static Mechanism* getPrimitiveMechanism(Primitive* p); + static const Mechanism* getConstPrimitiveMechanism(const Primitive* p); + + static Primitive* getRootMovingPrimitive(Primitive* p); + static const Primitive* getConstRootMovingPrimitive(const Primitive* p); + + /////////////////////////////////////////////////////////////// + // Assembly stuff + + static bool isMovingAssemblyRoot(const Assembly* a); + + static bool isComplexMovingMechanism(const Assembly* a); // i.e. - the assembly has children, connected by spring joints - complex networking issues + + static Assembly* getMovingAssemblyRoot(Assembly* a); + static const Assembly* getConstMovingAssemblyRoot(const Assembly* a); + + private: + template + inline void visitPrimitivesImpl(Func func, Assembly* a) { + a->visitPrimitives(func); + for (int i = 0; i < a->numChildren(); ++i) { + Assembly* child = a->getTypedChild(i); + visitPrimitivesImpl(func, child); + } + } + + public: + // Primitive Visiting Functions + template + inline void visitPrimitives(Func func) { + Assembly *root = getRootAssembly(); + RBXASSERT(root); + visitPrimitivesImpl(func, root); + } + }; + +} // namespace diff --git a/App/v8world/MegaClusterMesh.h b/App/v8world/MegaClusterMesh.h new file mode 100644 index 0000000..6c391c1 --- /dev/null +++ b/App/v8world/MegaClusterMesh.h @@ -0,0 +1,31 @@ +#pragma once + +/* + Utility class - holds MegaCluster dummy Meshes of same size for use by Geometry Pool. +*/ + +#include "Util/Memory.h" +#include "V8World/Mesh.h" + + +namespace RBX { + + namespace POLY { + + class MegaClusterMesh : public Allocator + { + private: + Mesh mesh; + Vector3 LocalCofM; + + public: + MegaClusterMesh(const Vector3& size) + { + mesh.makeBlock(size); + } + const Mesh* getMesh() const {return &mesh;} + Vector3 GetLocalCofMFromMesh( void ) { return LocalCofM; } + }; + + } // namespace POLY +} // namespace RBX \ No newline at end of file diff --git a/App/v8world/MegaClusterPoly.h b/App/v8world/MegaClusterPoly.h new file mode 100644 index 0000000..c06c24f --- /dev/null +++ b/App/v8world/MegaClusterPoly.h @@ -0,0 +1,86 @@ +#pragma once + +#include "V8World/Poly.h" +#include "V8World/GeometryPool.h" +#include "V8World/MegaClusterMesh.h" +#include "V8World/Primitive.h" +#include "V8World/TerrainPartition.h" + +class btConvexHullShape; + +namespace RBX { + + class MegaClusterInstance; + namespace Voxel { + class Grid; + } + + const float MC_SEARCH_RAY_MAX = 2048.0f; // was 500.0f, but normal mouse has range coded to be 2048.0f + const float MC_RAY_ZERO_SLOPE_TOLERANCE = .0005f; + const float MC_HUGE_VAL = 9999999; + + class MegaClusterPoly : public Poly + { + public: + MegaClusterPoly(Primitive* p); + ~MegaClusterPoly(); + + typedef GeometryPool MegaClusterMeshPool; + typedef Poly Super; + + /*override*/ virtual const G3D::Vector3& getSize() const {return Super::getSize();} + /*override*/ bool setUpBulletCollisionData(void) { return false; } + + private: + MegaClusterMeshPool::Token aMegaClusterMesh; + Primitive *myPrim; + + scoped_ptr myTerrainPartition; + + /*override*/ bool isGeometryOrthogonal( void ) const { return false; } + + std::vector bulletCellShapes; + + void createBulletCellShapes(void); + void createBulletCubeCell(void); + void createBulletVerticalWedgeCell(void); + void createBulletHorizontalWedgeCell(void); + void createBulletCornerWedgeCell(void); + void createBulletInverseCornerWedgeCell(void); + + bool hitLocationOnBlockCell(const RbxRay& rayInMe, const Vector3int16& testCell, Vector3& localHitPoint, Vector3& surfaceNormal, int& surfId, CoordinateFrame& surfaceCf) const; + bool hitLocationOnVerticalWedgeCell(const RbxRay& rayInMe, const Vector3int16& testCell, const int& orientation, Vector3& localHitPoint, Vector3& surfaceNormal, CoordinateFrame& surfaceCf) const; + bool hitLocationOnHorizontalWedgeCell(const RbxRay& rayInMe, const Vector3int16& testCell, const int& orientation, Vector3& localHitPoint, Vector3& surfaceNormal, CoordinateFrame& surfaceCf) const; + bool hitLocationOnCornerWedgeCell(const RbxRay& rayInMe, const Vector3int16& testCell, const int& orientation, Vector3& localHitPoint, Vector3& surfaceNormal, CoordinateFrame& surfaceCf) const; + bool hitLocationOnInverseCornerWedgeCell(const RbxRay& rayInMe, const Vector3int16& testCell, const int& orientation, Vector3& localHitPoint, Vector3& surfaceNormal, CoordinateFrame& surfaceCf) const; + + bool hitTestMC(const RbxRay& rayInMe, Vector3& localHitPoint, Vector3& surfaceNormal, int& surfId, CoordinateFrame& surfaceCf, float searchRayMax = MC_SEARCH_RAY_MAX, bool treatCellsAsBlocks = false, bool ignoreWater = false); + + protected: + // Geometry Overrides + /*override*/ virtual GeometryType getGeometryType() const {return GEOMETRY_MEGACLUSTER;} + /*override*/ Matrix3 getMoment(float mass) const { return Matrix3::identity(); } + /*override*/ Vector3 getCofmOffset() const { return Vector3::zero(); } + /*override*/ CoordinateFrame getSurfaceCoordInBody( const size_t surfaceId ) const; + /*override*/ size_t getFaceFromLegacyNormalId( const NormalId nId ) const; + + // Poly Overrides + /*override*/ void buildMesh(); + public: + /*override*/ bool hitTest(const RbxRay& rayInMe, Vector3& localHitPoint, Vector3& surfaceNormal, float searchRayMax = MC_SEARCH_RAY_MAX, bool treatCellsAsBlocks = false, bool ignoreWater = false); + /*override*/ bool findTouchingSurfacesConvex( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId ) const; + + virtual bool hitTestTerrain(const RbxRay& rayInMe, Vector3& localHitPoint, int& surfId, CoordinateFrame& surfCf); + + void findCellsTouchingGeometry( const CoordinateFrame& myCf, const Geometry& otherGeom, const CoordinateFrame& otherCf, std::vector* found ) const; + void findCellsTouchingGeometryWithBuffer( const float& buffer, const CoordinateFrame& myCf, const Geometry& otherGeom, const CoordinateFrame& otherCf, std::vector* found ) const; + bool findPlanarTouchesWithGeom( const CoordinateFrame& myCf, const Geometry& otherGeom, const CoordinateFrame& otherCf, std::vector* found ) const; + + std::vector findCellIntersectionWithGeom( const Vector3int16& cell, const CoordinateFrame& myCf, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t & otherFaceId ) const; + bool hasPlanarTouchWithGeom( const Vector3int16& cellIndex, const CoordinateFrame& myCf, const Geometry& otherGeom, const CoordinateFrame& otherCf ) const; + bool cellsInBoundingBox(const Vector3& min, const Vector3& max); + + btConvexHullShape* getBulletCellShape(Voxel::CellBlock shape); + }; + +} // namespace diff --git a/App/v8world/Mesh.h b/App/v8world/Mesh.h new file mode 100644 index 0000000..a77f573 --- /dev/null +++ b/App/v8world/Mesh.h @@ -0,0 +1,280 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "rbx/Debug.h" +#include "V8World/GeometryPool.h" + +namespace RBX { + + + namespace POLY { + class Edge; + class Face; + + /* + V - E - V + | | | + E - F - E + | | | + V - E - V + */ + + class Vertex { + private: + size_t id; + Vector3 offset; + std::vector edges; + + public: + Vertex() {} + + Vertex(size_t id, const Vector3& offset) : id(id), offset(offset) + {} + + const Vector3& getOffset() const {return offset;} + + int getId() const {return id;} + + void addEdge(Edge* value) { + RBXASSERT(std::find(edges.begin(), edges.end(), value) == edges.end()); + edges.push_back(value); + } + + Edge* findEdge(const Vertex* other); + + size_t numEdges() const {return edges.size();} + size_t numFaces() const {return edges.size();} + + Edge* getEdge(size_t i) const { + return edges[i]; + } + + static const Edge* recoverEdge(const Vertex* v0, const Vertex* v1); + + const Face* getFace(size_t i) const; + }; + + class Edge { + private: + size_t id; + const Vertex* vertex[2]; + const Face* forward; + const Face* backward; + + public: + Edge(size_t id, const Vertex* v0, const Vertex* v1) + : id(id) + , forward(NULL) + , backward(NULL) + { + vertex[0] = v0; + vertex[1] = v1; + } + + const Face* getForward() {return forward;} + const Face* getBackward() {return backward;} + + const Face* otherFace(const Face* test) const { + if (test == forward) { + return backward; + } + else { + RBXASSERT(test == backward); + return forward; + } + } + + bool contains(const Vertex* v) const { + return ((vertex[0] == v) || (vertex[1] == v)); + } + + void addFace(const Face* face) { + if (!forward) { + forward = face; + } + else { + RBXASSERT(face != forward); + RBXASSERT(!backward); + backward = face; + } + } + + const Vertex* getVertex(const Face* face, size_t id) const { + if (!face || (face == forward)) { + RBXASSERT(vertex[id]); + return vertex[id]; + } + else { + RBXASSERT(face == backward); + RBXASSERT(vertex[(id+1)%2]); + return vertex[(id+1)%2]; + } + } + + const Vector3& getVertexOffset(const Face* face, size_t id) const { + return getVertex(face, id)->getOffset(); + } + + const Face* getVertexFace(const Vertex* v) const { + if (v == vertex[0]) { + return forward; + } + else { + RBXASSERT(v == vertex[1]); + return backward; + } + } + + Vector3 computeNormal(const Face* face) const { + return (getVertexOffset(face, 1) - getVertexOffset(face, 0)).direction(); + } + + Line computeLine() const { + return Line::fromTwoPoints(vertex[0]->getOffset(), vertex[1]->getOffset()); + } + + size_t getId() const {return id;} + + bool pointInVaronoi(const Vector3& point) const; + }; + + class Face { + private: + size_t id; // id of face + std::vector edges; + Plane outwardPlane; + + bool lineCrossesExtrusionSide(const Vector3& p0, const Vector3& p1, size_t edgeId) const; + bool lineCrossesExtrusionSideBelow(const Vector3& p0, const Vector3& p1, size_t edgeId) const; + + bool pointInExtrusionSide(const Vector3& pointOnSide, const Plane& sidePlane, size_t edgeId) const; + + bool pointInInternalExtrusion(const Vector3& point) const { + return ((plane().distance(point) <= 0.0f) && pointInExtrusion(point)); + } + + public: + Face(size_t id, Edge* e0, Edge* e1, Edge* e2); + Face(size_t id, Edge* e0, Edge* e1, Edge* e2, Edge* e3); + Face( size_t id, std::vector& edgeList ); + void initPlane(); + + const Vertex* getVertex(int id) const { + return edges[id]->getVertex(this, 0); + } + + const Vector3& getVertexOffset(int id) const { + return getVertex(id)->getOffset(); + } + + const Vector3& normal() const { + return outwardPlane.normal(); + } + + size_t numEdges() const {return edges.size();} + size_t numVertices() const {return edges.size();} + + Edge* getEdge(size_t i) const { + return edges[i]; + } + + bool pointInExtrusion(const Vector3& point) const { + Vector3 pointOnPlane = plane().closestPoint(point); + return pointInFaceBorders(pointOnPlane); + } + + bool pointInFaceBorders(const Vector3& point) const; // point must be on face plane + + const Plane& plane() const { + RBXASSERT(edges.size() >= 3); + //RBXASSERT(!(outwardPlane == Plane())); + return outwardPlane; + } + + const Plane getSidePlane(size_t edgeId) const { + Edge* edge = getEdge(edgeId); + Vector3 sideVector = edge->computeNormal(this).cross(plane().normal()); + return Plane(sideVector, edge->getVertexOffset(this, 0)); + } + + int getInternalExtrusionIntersection(const Vector3& pBelowInside, const Vector3& pBelowOutside) const; + int findInternalExtrusionIntersection(const Vector3& p0, const Vector3& p1) const; + void findInternalExtrusionIntersections(const Vector3& p0, const Vector3& p1, int& side0, int& side1) const; + + size_t getId() const {return id;} + + Vector3 getCentroid( void ) const; + void getOrientedBoundingBox( const Vector3& xDir, const Vector3& yDir, Vector3& boxMin, Vector3& boxMax, Vector3& boxCenter ) const; + + }; + + class Mesh { + private: + std::vector vertices; + std::vector edges; + std::vector faces; + + void clear(); + + void addVertex(float x, float y, float z); + void addFace(size_t i, size_t j, size_t k); + void addFace(size_t i, size_t j, size_t k, size_t l); + void addFace( int numVerts, int vertIndexList[], bool reverseOrder ); + + Edge* findOrMakeEdge(size_t v0, size_t v1); + Edge* addEdge(Vertex* vert0, Vertex* vert1); + + bool lineIntersectsFace(const Line& line, const Face* face) const; + bool rayIntersectsFace(const RbxRay& ray, const Face* face, Vector3& intersection) const; + + public: + Mesh() {} + + size_t numFaces() const {return faces.size();} + const POLY::Face* getFace(int i) const {return &faces[i];} + + size_t numVertices() const {return vertices.size();} + const POLY::Vertex* getVertex(int i) const {return &vertices[i];} + + size_t numEdges() const {return edges.size();} + const POLY::Edge* getEdge(int i) const {return &edges[i];} + + bool containsFace(const Face* face) const { + for (size_t i = 0; i < numFaces(); ++i) { + if (face == getFace(i)) { + return true; + } + } + return false; + } + + const POLY::Face* findFace(size_t i0, size_t i1, size_t i2); + + const Vertex* farthestVertex(const Vector3& direction) const; + + bool pointInMesh(const Vector3& point) const; + + const Face* findFaceIntersection(const Vector3& inside, const Vector3& outside) const; + + void findFaceIntersections(const Vector3& p0, const Vector3& p1, const Face* &f0, const Face* &f1) const; + + bool hitTest(const RbxRay& ray, Vector3& hitPoint, Vector3& surfaceNormal) const; + + void makeWedge(const Vector3& size); + + void makePrism(const Vector3_2Ints& params, Vector3& cofm); + void makePyramid(const Vector3_2Ints& params, Vector3& cofm); + void makeParallelRamp(const Vector3& size, Vector3& cofm); + void makeRightAngleRamp(const Vector3& size, Vector3& cofm); + void makeCornerWedge(const Vector3& size, Vector3& cofm); + + + void makeBlock(const Vector3& size); + void makeCell(const Vector3& size, const Vector3& offset); + void makeVerticalWedgeCell(const Vector3& size, const Vector3& offset, const int& orient); + void makeHorizontalWedgeCell(const Vector3& size, const Vector3& offset, const int& orient); + void makeCornerWedgeCell(const Vector3& size, const Vector3& offset, const int& orient); + void makeInverseCornerWedgeCell(const Vector3& size, const Vector3& offset, const int& orient); + }; + } // namespace POLY +} // namespace diff --git a/App/v8world/Motor6DJoint.h b/App/v8world/Motor6DJoint.h new file mode 100644 index 0000000..5e48954 --- /dev/null +++ b/App/v8world/Motor6DJoint.h @@ -0,0 +1,61 @@ +#pragma once + +#include "V8World/Joint.h" + +namespace RBX { + + class D6Link; + + class Motor6DJoint : public Joint + { + private: + D6Link* link; + + /////////////////////////////////////////////////// + // Joint + /*override*/ JointType getJointType() const {return Joint::MOTOR_6D_JOINT;} + /*override*/ bool isBroken() const {return false;} + /*override*/ bool isAligned(); + + Vector3 poseOffsetDelta; + Vector3 poseAxisAngleDelta; + float poseMaskWeight; + int poseFreshness; + Vector3 currentOffset; + Vector3 currentAxisAngle; + + int getParentId() const; + + void setJointOffsetCFrame(const Vector3 offset, const Vector3 axisAngle); + + public: + float maxZAngleVelocity; // for support of legacy animate scripts + float desiredZAngle; // + float getCurrentZAngle() const;// + void setCurrentZAngle(float value); + Vector3 getCurrentOffset() const {return currentOffset;} + Vector3 getCurrentAngle() const {return currentAxisAngle;} + bool setCurrentOffsetAngle(const Vector3 offset, const Vector3 axisAngle); + void applyPose(const Vector3& poseOffset, const Vector3& poseAxisAngle, float poseWeight, float maskWeight); + + CoordinateFrame getMeInOther(Primitive* me); + + /*override*/ bool canStepUi() const {return true;} + /*override*/ bool stepUi(double distributedGameTime); + + Motor6DJoint(); + + ~Motor6DJoint(); + + size_t hashCode() const; + + /*override*/ Link* resetLink(); + + static bool isMotor6DJoint(const Edge* e) { + return ( isJoint(e) + && rbx_static_cast(e)->getJointType() == Joint::MOTOR_6D_JOINT); + } + }; + +} // namespace + diff --git a/App/v8world/MotorJoint.h b/App/v8world/MotorJoint.h new file mode 100644 index 0000000..f8f004b --- /dev/null +++ b/App/v8world/MotorJoint.h @@ -0,0 +1,59 @@ +#pragma once + +#include "V8World/Joint.h" + +namespace RBX { + + class RevoluteLink; + + class MotorJoint : public Joint + { + private: + RevoluteLink* link; + + /////////////////////////////////////////////////// + // Joint + /*override*/ JointType getJointType() const {return Joint::MOTOR_1D_JOINT;} + /*override*/ bool isBroken() const {return false;} + /*override*/ bool isAligned(); + + float currentAngle; + float poseAngleDelta; + float poseMaskWeight; + int poseFreshness; + + int getParentId() const; + + void setJointAngle(float value); + + public: + // tweak this to adjust how long a pose stays applied in the absence of a fresh call to applyPose() + static const int poseDuration = 32; + + float maxVelocity; + float desiredAngle; + float getCurrentAngle() const {return currentAngle;} + bool setCurrentAngle(float value); + void applyPose(float poseAngle, float poseWeight, float maskWeight); + + CoordinateFrame getMeInOther(Primitive* me); + + /*override*/ bool canStepUi() const {return true;} + /*override*/ bool stepUi(double distributedGameTime); + + MotorJoint(); + + ~MotorJoint(); + + size_t hashCode() const; + + /*override*/ Link* resetLink(); + + static bool isMotorJoint(const Edge* e) { + return ( isJoint(e) + && rbx_static_cast(e)->getJointType() == Joint::MOTOR_1D_JOINT); + } + }; + +} // namespace + diff --git a/App/v8world/MovingAssemblyStage.h b/App/v8world/MovingAssemblyStage.h new file mode 100644 index 0000000..1a716fc --- /dev/null +++ b/App/v8world/MovingAssemblyStage.h @@ -0,0 +1,71 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" +#include "V8World/Joint.h" +#include "v8world/Assembly.h" + + +namespace RBX { + class Assembly; + + class MovingAssemblyStage : public IWorldStage + { + private: + /////////////////////////////////////// + typedef boost::intrusive::list > Joints; + Joints uiStepJoints; + + boost::unordered_set animatedJoints; + + typedef std::set Assemblies; + Assemblies movingGroundedAssemblies; + Assemblies movingAnimatedAssemblies; + + void addJoint(Joint* j); + void removeJoint(Joint* j); + + void jointsStepUiInternal(double distributedGameTime, Joint* j, bool fromAnimation); + + public: + MovingAssemblyStage(IStage* upstream, World* world); + + ~MovingAssemblyStage(); + + /*override*/ IStage::StageType getStageType() const {return IStage::MOVING_ASSEMBLY_STAGE;} + + /*override*/ void onEdgeAdded(Edge* e); + /*override*/ void onEdgeRemoving(Edge* e); + + void addAnimatedJoint(Joint* j); + void removeAnimatedJoint(Joint* j); + + void jointsStepUi(double distributedGameTime); + + void onSimulateAssemblyAdded(Assembly* a); + void onSimulateAssemblyRemoving(Assembly* a); + + void addMovingGroundedAssembly(Assembly* a); + void removeMovingGroundedAssembly(Assembly* a); + + void addMovingAnimatedAssembly(Assembly* a); + void removeMovingAnimatedAssembly(Assembly *a); + + int getMovingGroundedAssembliesSize() { return movingGroundedAssemblies.size(); } + + Assemblies::iterator getMovingGroundedAssembliesBegin() { + return movingGroundedAssemblies.begin(); + } + Assemblies::iterator getMovingGroundedAssembliesEnd() { + return movingGroundedAssemblies.end(); + } + const Assemblies& getMovingGroundedAssemblies() { + return movingGroundedAssemblies; + } + + const Assemblies& getMovingAnimatedAssemblies() { + return movingAnimatedAssemblies; + } + }; +} // namespace diff --git a/App/v8world/MovingStage.h b/App/v8world/MovingStage.h new file mode 100644 index 0000000..ed519c0 --- /dev/null +++ b/App/v8world/MovingStage.h @@ -0,0 +1,34 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" + +namespace RBX { + + class Mechanism; + class Assembly; + + class MovingStage : public IWorldStage + { + private: + class SpatialFilter* getSpatialFilter(); + + public: + /////////////////////////////////////////// + // IStage + MovingStage(IStage* upstream, World* world); + + ~MovingStage(); + + /*override*/ IStage::StageType getStageType() const {return IStage::MOVING_STAGE;} + + ///////////////////////////////////////////// + // From the Joint Stage + // + void onMechanismAdded(Mechanism* a); + void onMechanismRemoving(Mechanism* a); + }; +} // namespace + + diff --git a/App/v8world/MultiJoint.h b/App/v8world/MultiJoint.h new file mode 100644 index 0000000..7d777fd --- /dev/null +++ b/App/v8world/MultiJoint.h @@ -0,0 +1,63 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/Joint.h" +#include "Util/Extents.h" +#include "Util/Face.h" + +namespace RBX { + + class Kernel; + class Channel; + class Connector; + class Point; + class Primitive; + + class MultiJoint : public Joint + { + private: + typedef Joint Super; + int numConnector; + Point* point[8]; + + bool pointsAligned() const; + void init(int numBreaking); + int numBreakingConnectors; + + bool validateMultiJoint(); + + protected: + + ////////////////////////////////////////////////////////////// + // + // Edge + /*override*/ void putInKernel(Kernel* kernel); + /*override*/ void removeFromKernel(); + + ////////////////////////////////////////////////////////////// + // + // Joint + /*override*/ bool isBroken() const; + + Connector* connector[4]; // NormalBreakConnector + + void addToMultiJoint(Point* point0, Point* point1, Connector* _connector); + Point* getPoint(int id); + Connector* getConnector(int id); + + float getJointK(); + + MultiJoint(int numBreaking); + + MultiJoint( + Primitive* p0, + Primitive* p1, + const CoordinateFrame& jointCoord0, + const CoordinateFrame& jointCoord1, + int numBreaking); + + ~MultiJoint(); + }; + +} // namespace diff --git a/App/v8world/ParallelRampMesh.h b/App/v8world/ParallelRampMesh.h new file mode 100644 index 0000000..bc93a7a --- /dev/null +++ b/App/v8world/ParallelRampMesh.h @@ -0,0 +1,31 @@ +#pragma once + +/* + Utility class - holds ParallelRamp Meshes of same size for use by Geometry Pool. +*/ + +#include "Util/Memory.h" +#include "V8World/Mesh.h" + + +namespace RBX { + + namespace POLY { + + class ParallelRampMesh : public Allocator + { + private: + Mesh mesh; + Vector3 LocalCofM; + + public: + ParallelRampMesh(const Vector3& size) + { + mesh.makeParallelRamp(size, LocalCofM); + } + const Mesh* getMesh() const {return &mesh;} + const Vector3& GetLocalCofMFromMesh() const { return LocalCofM; } + }; + + } // namespace POLY +} // namespace RBX \ No newline at end of file diff --git a/App/v8world/ParallelRampPoly.h b/App/v8world/ParallelRampPoly.h new file mode 100644 index 0000000..fade09a --- /dev/null +++ b/App/v8world/ParallelRampPoly.h @@ -0,0 +1,30 @@ +#pragma once + +#include "V8World/Poly.h" +#include "V8World/GeometryPool.h" +#include "V8World/ParallelRampMesh.h" +#include "V8World/BlockMesh.h" + +namespace RBX { + + class ParallelRampPoly : public Poly { + public: + typedef GeometryPool ParallelRampMeshPool; + + /*override*/ Matrix3 getMoment(float mass) const; + /*override*/ Vector3 getCofmOffset() const; + /*override*/ bool isGeometryOrthogonal( void ) const { return false; } + /*override*/ bool setUpBulletCollisionData(void) { return false; } + + private: + ParallelRampMeshPool::Token aParallelRampMesh; + + protected: + // Geometry Overrides + /*override*/ virtual GeometryType getGeometryType() const {return GEOMETRY_PARALLELRAMP;} + + // Poly Overrides + /*override*/ void buildMesh(); + }; + +} // namespace diff --git a/App/v8world/Poly.h b/App/v8world/Poly.h new file mode 100644 index 0000000..d5ebc4a --- /dev/null +++ b/App/v8world/Poly.h @@ -0,0 +1,71 @@ +#pragma once + +#include "V8World/Geometry.h" +#include "V8World/Mesh.h" + +namespace RBX { + + namespace POLY { + class Mesh; + } + + + class Poly : public Geometry { + private: + typedef Geometry Super; + float centerToCornerDistance; + + protected: + const POLY::Mesh* mesh; + + /*override*/ void setSize(const G3D::Vector3& _size); + + float getCenterToCornerDistance() const {return centerToCornerDistance;} + + Vector3 getCenterToCornerWorst() const {return Vector3(centerToCornerDistance, centerToCornerDistance, centerToCornerDistance);} + + /*implement*/ virtual void buildMesh() = 0; + + public: + Poly() : mesh(NULL) {} + ~Poly() {} + + // Geometry Overrides + /*override*/ virtual CollideType getCollideType() const {return COLLIDE_POLY;} + + /*override*/ virtual bool hitTest(const RbxRay& rayInMe, Vector3& localHitPoint, Vector3& surfaceNormal); + + /*override*/ float getRadius() const {return centerToCornerDistance;} + + /*override*/ Vector3 getCenterToCorner(const Matrix3& rotation) const {return getCenterToCornerWorst();} + + /*override*/ Vector3 getCofmOffset() const; + + /*override*/ Matrix3 getMoment(float mass) const; + + /*override*/ bool collidesWithGroundPlane(const CoordinateFrame& c, float yHeight) const; + + const POLY::Mesh* getMesh() const {return mesh;} + + // Dragger/joiner support + size_t closestSurfaceToPoint( const Vector3& pointInBody ) const; + Plane getPlaneFromSurface( const size_t surfaceId ) const; + virtual CoordinateFrame getSurfaceCoordInBody( const size_t surfaceId ) const; + Vector3 getSurfaceNormalInBody( const size_t surfaceId ) const; + size_t getMostAlignedSurface( const Vector3& vecInWorld, const G3D::Matrix3& objectR ) const; + int getNumSurfaces( void ) const { return mesh->numFaces(); } + Vector3 getSurfaceVertInBody( const size_t surfaceId, const int vertId ) const; + int getNumVertsInSurface( const size_t surfaceId ) const; + bool vertOverlapsFace( const Vector3& pointInBody, const size_t surfaceId ) const; + + /*override*/virtual bool findTouchingSurfacesConvex( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId ) const; + /*override*/virtual bool FacesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const; + /*override*/virtual bool FaceVerticesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const; + /*override*/virtual bool FaceEdgesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const; + + + /*override*/ std::vector polygonIntersectionWithFace( const std::vector& polygonInBody, const size_t surfaceId ) const; + + }; + +} // namespace diff --git a/App/v8world/PolyCellContact.h b/App/v8world/PolyCellContact.h new file mode 100644 index 0000000..b08675f --- /dev/null +++ b/App/v8world/PolyCellContact.h @@ -0,0 +1,158 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/CellContact.h" +#include "V8World/PolyPolyContact.h" +#include "V8Kernel/ContactParams.h" +#include "V8World/Mesh.h" + +namespace RBX { + + class Poly; + class PolyCellContact; + class FaceVertexConnector; + class FaceEdgeConnector; + class EdgeEdgeConnector; + + namespace POLY { + class Face; + class Edge; + class Vertex; + class Mesh; + } + + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + class PolyCellPair + { + protected: + Primitive* primitive[2]; + ContactParams contactParams; + bool swapPrims; + + PolyCellContact* myPCContact; + + const Poly* poly0() const; + const Poly* poly1() const; + + const Poly* poly(size_t i) { + return (i == 0) ? poly0() : poly1(); + } + + /*implement*/ virtual bool isFaceFace() const = 0; + /*override*/ virtual bool pairIsValid() { return true; } + + public: + PolyCellPair(Primitive* p0, Primitive* p1, const ContactParams& contactParams, PolyCellContact* aPCContact, bool swap) + : contactParams(contactParams) + { + primitive[0] = p0; + primitive[1] = p1; + myPCContact = aPCContact; + swapPrims = swap; + } + virtual ~PolyCellPair() {} + + /*implement*/ virtual PolyCellPair* allocateClone() = 0; + /*implement*/ virtual float test() = 0; + /*implement*/ virtual void loadConnectors(ConnectorArray& newConnectors) = 0; + + bool match(const PolyCellPair* other) const { + return ( (isFaceFace() == other->isFaceFace()) + && (primitive[0] == other->primitive[0]) ); + } + }; + + class CellFaceFacePair : public PolyCellPair + { + private: + const POLY::Face* mainFace; + const POLY::Face* otherFace; + + const POLY::Face* face(size_t i) { + return (i == 0) ? mainFace : otherFace; + } + + const Poly* facePoly() const {return poly0();} + const Poly* otherPoly() const {return poly1();} + + typedef enum {ABOVE_INSIDE, ABOVE_OUTSIDE, BELOW_INSIDE, BELOW_OUTSIDE} VertexStatus; + + //void computeVertices(FixedArray& verticesInObject, const CoordinateFrame& otherInMe); + void computeVertices(FixedArray& verticesInObject, const CoordinateFrame& otherInMe); + //float closestVertex(const POLY::Face* face, const FixedArray& verticesInObject, const POLY::Vertex* &closestVertex); + float closestVertex(const POLY::Face* face, const FixedArray& verticesInObject, const POLY::Vertex* &closestVertex); + + const POLY::Face* findOtherFace(const POLY::Vertex* closeVertex); + + //bool loadVertices(FixedArray* vertexStatus, CoordinateFrame* vertexInFace, ConnectorArray& newConnectors); + bool loadVertices(FixedArray* vertexStatus, CoordinateFrame* vertexInFace, ConnectorArray& newConnectors); + //bool testVerticesInside(size_t faceId, FixedArray& vertexStatus, const CoordinateFrame& vertexInFace, ConnectorArray& newConnectors); + bool testVerticesInside(size_t faceId, FixedArray& vertexStatus, const CoordinateFrame& vertexInFace, ConnectorArray& newConnectors); + VertexStatus vertexInPoly(const POLY::Face* planeFace, const POLY::Mesh* planeMesh, const POLY::Vertex* vertex, const CoordinateFrame& otherInMe); + + void vertexInside( + Primitive* pFace, + Primitive* pVertex, + const POLY::Vertex* inside, + const POLY::Face* planeFace, + ConnectorArray& newConnectors); + + void checkOneSideIntersection(const POLY::Vertex* v0, const POLY::Vertex* v1, const CoordinateFrame& otherInMe, ConnectorArray& newConnectors); + void validateOneSideIntersection(const POLY::Vertex* belowInside, const POLY::Vertex* belowOutside, const CoordinateFrame& otherInMe, ConnectorArray& newConnectors); + void checkTwoSideIntersections(const POLY::Vertex* v0, const POLY::Vertex* v1, const CoordinateFrame& otherInMe, ConnectorArray& newConnectors); + + FaceEdgeConnector* newFaceEdgeConnector(size_t mainFaceEdgeId, const POLY::Vertex* v0, const POLY::Vertex* v1); + + /*override*/ bool isFaceFace() const {return true;} + /*override*/ PolyCellPair* allocateClone(); + /*override*/ float test(); + /*override*/ void loadConnectors(ConnectorArray& newConnectors); + + public: + CellFaceFacePair(Primitive* p0, Primitive* p1, const ContactParams& contactParams, PolyCellContact* aPCContact, bool swap); + bool pairIsValid(void); + + }; + + class CellEdgeEdgePair : public PolyCellPair + { + private: + const POLY::Edge* bestEdge0; + const POLY::Edge* bestEdge1; + + void computeMinMax(const Plane& planeInMesh, const POLY::Mesh* mesh, float& min, float& max); + + EdgeEdgeConnector* newEdgeEdgeConnector(); + + /*override*/ bool isFaceFace() const {return false;} + /*override*/ PolyCellPair* allocateClone(); + /*override*/ float test(); + /*override*/ void loadConnectors(ConnectorArray& newConnectors); + + public: + CellEdgeEdgePair(Primitive* p0, Primitive* p1, const ContactParams& contactParams, PolyCellContact* aPCContact, bool swap); + }; + + class PolyCellContact + : public CellMeshContact + , public Allocator + { + private: + PolyCellPair* bestPair; + + void findBestPair(); + void resetBestPair(PolyCellPair* pairOnStack); + + /*override*/ void findClosestFeatures(ConnectorArray& newConnectors); + + public: + PolyCellContact(Primitive* p0, Primitive* p1, const Vector3int16& cell); + ~PolyCellContact(); + + static float epsilonDistance(); // distance to switch + void generateDataForMovingAssemblyStage(void); /*override*/ + }; + +} // namespace \ No newline at end of file diff --git a/App/v8world/PolyContact.h b/App/v8world/PolyContact.h new file mode 100644 index 0000000..777fd78 --- /dev/null +++ b/App/v8world/PolyContact.h @@ -0,0 +1,47 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/Contact.h" + +namespace RBX { + + class PolyConnector; + + //typedef RBX::FixedArray ConnectorArray; // TODO - should only ever need 8 + typedef RBX::FixedArray ConnectorArray; // TODO - should only ever need 8 + + class PolyContact : public Contact + { + private: + ConnectorArray polyConnectors; + + void removeAllConnectorsFromKernel(); + void putAllConnectorsInKernel(); + void updateClosestFeatures(); + float worstFeatureOverlap(); + void deleteConnectors(ConnectorArray& deleteConnectors); + void matchClosestFeatures(ConnectorArray& newConnectors); + PolyConnector* matchClosestFeature(PolyConnector* newConnector); + void updateContactPoints(); + + // Contact + /*override*/ void deleteAllConnectors(); + /*override*/ int numConnectors() const {return polyConnectors.size();} + /*override*/ ContactConnector* getConnector(int i); + /*override*/ bool computeIsColliding(float overlapIgnored); + /*override*/ bool stepContact(); + + /*implement*/ virtual void findClosestFeatures(ConnectorArray& newConnectors) = 0; + + public: + PolyContact(Primitive* p0, Primitive* p1) + : Contact(p0, p1) + {} + + ~PolyContact(); + }; + + + +} // namespace \ No newline at end of file diff --git a/App/v8world/PolyPolyContact.h b/App/v8world/PolyPolyContact.h new file mode 100644 index 0000000..9897217 --- /dev/null +++ b/App/v8world/PolyPolyContact.h @@ -0,0 +1,157 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/PolyContact.h" +#include "V8Kernel/ContactParams.h" + +namespace RBX { + + class Poly; + class PolyPolyContact; + class FaceVertexConnector; + class FaceEdgeConnector; + class EdgeEdgeConnector; + + namespace POLY { + class Face; + class Edge; + class Vertex; + class Mesh; + } + + + + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + + class PolyPair + { + protected: + Primitive* primitive[2]; + ContactParams contactParams; + + const Poly* poly0() const; + const Poly* poly1() const; + + const Poly* poly(size_t i) { + return (i == 0) ? poly0() : poly1(); + } + + /*implement*/ virtual bool isFaceFace() const = 0; + + public: + PolyPair(Primitive* p0, Primitive* p1, const ContactParams& contactParams) + : contactParams(contactParams) + { + primitive[0] = p0; + primitive[1] = p1; + } + virtual ~PolyPair() {} + + /*implement*/ virtual PolyPair* allocateClone() = 0; + /*implement*/ virtual float test() = 0; + /*implement*/ virtual void loadConnectors(ConnectorArray& newConnectors) = 0; + + bool match(const PolyPair* other) const { + return ( (isFaceFace() == other->isFaceFace()) + && (primitive[0] == other->primitive[0]) ); + } + }; + + + class FaceFacePair : public PolyPair + { + private: + const POLY::Face* mainFace; + const POLY::Face* otherFace; + const POLY::Face* nextBestOtherFace; + + const POLY::Face* face(size_t i) { + return (i == 0) ? mainFace : otherFace; + } + + const Poly* facePoly() const {return poly0();} + const Poly* otherPoly() const {return poly1();} + + typedef enum {ABOVE_INSIDE, ABOVE_OUTSIDE, BELOW_INSIDE, BELOW_OUTSIDE} VertexStatus; + + //void computeVertices(FixedArray& verticesInObject, const CoordinateFrame& otherInMe); + void computeVertices(FixedArray& verticesInObject, const CoordinateFrame& otherInMe); + //float closestVertex(const POLY::Face* face, const FixedArray& verticesInObject, const POLY::Vertex* &closestVertex); + float closestVertex(const POLY::Face* face, const FixedArray& verticesInObject, const POLY::Vertex* &closestVertex); + + const POLY::Face* findOtherFace(const POLY::Vertex* closeVertex); + + //bool loadVertices(FixedArray* vertexStatus, CoordinateFrame* vertexInFace, ConnectorArray& newConnectors); + bool loadVertices(FixedArray* vertexStatus, CoordinateFrame* vertexInFace, ConnectorArray& newConnectors); + //bool testVerticesInside(size_t faceId, FixedArray& vertexStatus, const CoordinateFrame& vertexInFace, ConnectorArray& newConnectors); + bool testVerticesInside(size_t faceId, FixedArray& vertexStatus, const CoordinateFrame& vertexInFace, ConnectorArray& newConnectors); + VertexStatus vertexInPoly(const POLY::Face* planeFace, const POLY::Mesh* planeMesh, const POLY::Vertex* vertex, const CoordinateFrame& otherInMe); + + void vertexInside( + Primitive* pFace, + Primitive* pVertex, + const POLY::Vertex* inside, + const POLY::Face* planeFace, + ConnectorArray& newConnectors); + + void checkOneSideIntersection(const POLY::Vertex* v0, const POLY::Vertex* v1, const CoordinateFrame& otherInMe, ConnectorArray& newConnectors); + void validateOneSideIntersection(const POLY::Vertex* belowInside, const POLY::Vertex* belowOutside, const CoordinateFrame& otherInMe, ConnectorArray& newConnectors); + void checkTwoSideIntersections(const POLY::Vertex* v0, const POLY::Vertex* v1, const CoordinateFrame& otherInMe, ConnectorArray& newConnectors); + + FaceEdgeConnector* newFaceEdgeConnector(size_t mainFaceEdgeId, const POLY::Vertex* v0, const POLY::Vertex* v1); + + /*override*/ bool isFaceFace() const {return true;} + /*override*/ PolyPair* allocateClone(); + /*override*/ float test(); + /*override*/ void loadConnectors(ConnectorArray& newConnectors); + + public: + FaceFacePair(Primitive* p0, Primitive* p1, const ContactParams& contactParams); + void setOtherFace(const POLY::Face* aFace) { otherFace = aFace; } + const POLY::Face* getNextBestOtherFace(void) { return nextBestOtherFace; } + }; + + class EdgeEdgePair : public PolyPair + { + private: + const POLY::Edge* bestEdge0; + const POLY::Edge* bestEdge1; + + void computeMinMax(const Plane& planeInMesh, const POLY::Mesh* mesh, float& min, float& max); + + EdgeEdgeConnector* newEdgeEdgeConnector(); + + /*override*/ bool isFaceFace() const {return false;} + /*override*/ PolyPair* allocateClone(); + /*override*/ float test(); + /*override*/ void loadConnectors(ConnectorArray& newConnectors); + + public: + EdgeEdgePair(Primitive* p0, Primitive* p1, const ContactParams& contactParams); + }; + + + + class PolyPolyContact + : public PolyContact + , public Allocator + { + private: + PolyPair* bestPair; + + void findBestPair(); + void resetBestPair(PolyPair* pairOnStack); + + /*override*/ void findClosestFeatures(ConnectorArray& newConnectors); + public: + PolyPolyContact(Primitive* p0, Primitive* p1); + ~PolyPolyContact(); + + static float epsilonDistance(); // distance to switch + + void generateDataForMovingAssemblyStage(void); /*override*/ + }; + +} // namespace \ No newline at end of file diff --git a/App/v8world/Primitive.h b/App/v8world/Primitive.h new file mode 100644 index 0000000..7169452 --- /dev/null +++ b/App/v8world/Primitive.h @@ -0,0 +1,430 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8Kernel/BodyPvSetter.h" +#include "V8World/Geometry.h" +#include "V8World/Edge.h" +#include "V8World/SurfaceData.h" +#include "V8World/IMoving.h" +#include "V8World/BasicSpatialHashPrimitive.h" +#include "Util/SurfaceType.h" +#include "Util/Extents.h" +#include "Util/Face.h" +#include "Util/NormalId.h" +#include "Util/ComputeProp.h" +#include "Util/G3DCore.h" +#include "Util/Guid.h" +#include "Util/IndexArray.h" +#include "Util/SpanningNode.h" +#include "Util/SystemAddress.h" +#include "Util/CompactEnum.h" +#include "util/PhysicalProperties.h" +#include "util/PartMaterial.h" +#include +#include + +namespace RBX { + enum NetworkOwnership + { + NetworkOwnership_Auto = 0, + NetworkOwnership_Manual = 1 + }; + + class Body; + class Clump; + class Assembly; + class Mechanism; + class World; + template class SpatialHash; + class Contact; + class Joint; + struct RootPrimitiveOwnershipData; + + class EdgeList + { + private: + struct Entry + { + Edge* edge; + Primitive* other; + }; + + Primitive* owner; + std::vector list; + + public: + EdgeList(Primitive* owner) : owner(owner) + {} + + ~EdgeList() { + RBXASSERT(list.size() == 0); + } + + int size() const {return list.size();} + + Edge* getEdge(int i) const + { + RBXASSERT_VERY_FAST(unsigned(i) < list.size()); + RBXASSERT_VERY_FAST(list[i].edge->otherPrimitive(owner) == list[i].other); + return list[i].edge; + } + + Primitive* getOther(int i) const + { + RBXASSERT_VERY_FAST(unsigned(i) < list.size()); + RBXASSERT_VERY_FAST(list[i].edge->otherPrimitive(owner) == list[i].other); + return list[i].other; + } + + Edge* getFirst() const {return (list.size() > 0) ? list[0].edge : NULL;} + + Edge* getNext(const Primitive* p, Edge* e) const; + + void insertEdge(Edge* e); + void removeEdge(Edge* e); + }; + + class Primitive : public IPipelined + , public SpanningNode + , public BodyPvSetter + , public BasicSpatialHashPrimitive + { + template friend class SpatialHash; + + + public: + static bool allowSleep; + + typedef enum {DYNAMICS_ENGINE, HUMANOID_ENGINE} EngineType; + typedef enum {DEFAULT_SIZE, TORSO_SIZE, ROOT_SIZE, SEAT_SIZE} SizeMultiplier; // overweights torsos and seats to make them roots of the spanning tree + + // bullet related + void updateBulletCollisionObject(void); + + // Moved to public for CSG + Vector3 clipToSafeSize(const Vector3& newSize); + + // WORLD access here + public: + int& worldIndexFunc() {return worldIndex;} + + private: + void onChangedInKernel(); + + // For fuzzyExtents - when updated + static int fuzzyExtentsReset() {return -2;} // out of synch with body -1; + + Extents computeFuzzyExtents(); + void setFixed(bool newAnchoredProperty, bool newDragging); + + float computeJointK(); + + Geometry* newGeometry(Geometry::GeometryType geometryType); + + public: + Primitive(Geometry::GeometryType geometryType); + virtual ~Primitive(); + + const Guid& getGuid() const; + void setGuid(const Guid& value); + + unsigned int getSizeMultiplier() const; + void setSizeMultiplier(SizeMultiplier value); + + void calculateSortSize(); + unsigned int getSortSize(); + + World* getWorld() const {return world;} + void setWorld(World* _world) {world = _world;} + + // Clump + Clump* getClump(); + const Clump* getConstClump() const; + Assembly* getAssembly(); + const Assembly* getConstAssembly() const; + Mechanism* getMechanism(); + const Mechanism* getConstMechanism() const; + + // Geometry + Geometry* getGeometry() {return geometry;} + const Geometry* getConstGeometry() const {return geometry;} + + void resetGeometryType(Geometry::GeometryType geometryType); + void setGeometryType(Geometry::GeometryType geometryType); + Geometry::GeometryType getGeometryType() const; + Geometry::CollideType getCollideType() const; + + // Body + Body* getBody() {return body;} + const Body* getConstBody() const {return body;} + + // Class PartInstance + void setOwner(IMoving* set); + IMoving* getOwner() const {return myOwner;} + + // Access Mechanism Root + Primitive* getMechRoot(); + + // Access Root Moving Primitive + Primitive* getRootMovingPrimitive(); + + // Find if current primitive is Ancestor of Primitive + bool isAncestorOf(Primitive* prim); + + // Network + const RBX::SystemAddress getNetworkOwner() const {return networkOwner;} + void setNetworkOwner(const RBX::SystemAddress value) {networkOwner = value;} + + const NetworkOwnership getNetworkOwnershipRuleInternal() const { return networkOwnershipRule; } + void setNetworkOwnershipRuleInternal(NetworkOwnership value) { networkOwnershipRule = value; } + + bool getNetworkIsSleeping() const {return networkIsSleeping;} + void setNetworkIsSleeping(bool value, Time wakeupNow); + + /////////////////////////////////////////////// + // + static void onNewOverlap(Primitive* p0, Primitive* p1); + static void onStopOverlap(Primitive* p0, Primitive* p1); + + /////////////////////////////////////////////// + // Properties + // + // Position + const PV& getPV() const; + const CoordinateFrame& getCoordinateFrame() const; + const CoordinateFrame& getCoordinateFrameUnsafe(); // Faster: Thread must hold writer lock. + + void setCoordinateFrame(const CoordinateFrame& cFrame); + void setPV(const PV& newPv); + + // Velocity + void setVelocity(const Velocity& vel); + void zeroVelocity(); // doesn't tickle primitive + + // Mass + void setMassInertia(float mass); + + // Density/Specific Gravity + void setSpecificGravity(float value); + float getSpecificGravity() const { return specificGravity; } + + // Dragging + void setDragging(bool value); + bool getDragging() const {return dragging;} + + // Anchored + void setAnchoredProperty(bool value); + bool getAnchoredProperty() const {return anchoredProperty;} + + void updateMassValues(bool physicalPropertiesEnabled); + float getCalculateMass(bool physicalPropertiesEnabled); + + // EngineType + void setEngineType(EngineType value); + EngineType getEngineType() const {return engineType;} + + // Fixed + bool requestFixed() const {return (dragging || anchoredProperty);} + + // Collide + void setPreventCollide(bool _preventCollide); + bool getPreventCollide() const {return preventCollide;} + + bool getCanCollide() const {return !dragging && !preventCollide;} + + // CanThrottle + void setCanThrottle(bool value); + bool getCanThrottle() const; + + // PartMaterial + void setPartMaterial(PartMaterial _material); + PartMaterial getPartMaterial() const { return material; } + + // Friction + void setFriction(float _friction); + float getFriction() const {return friction;} + + // Elasticity + void setElasticity(float elasticity); + float getElasticity() const {return elasticity;} + + void setPhysicalProperties(const PhysicalProperties& _physProp); + const PhysicalProperties& getPhysicalProperties() const { return customPhysicalProperties; } + + // Buouyancy + void onBuoyancyChanged( bool value ); + + // Parameters + void setGeometryParameter(const std::string& parameter, int value); + int getGeometryParameter(const std::string& parameter) const; + + // Size and Extents - local + void setSize(const G3D::Vector3& size); + const Vector3& getSize() const {return geometry->getSize();} + + virtual float getRadius() const {return geometry->getRadius();} + + float getPlanarSize() const {return Math::planarSize(getSize());} + + Extents getExtentsLocal() const { + Vector3 halfSize = geometry->getSize() * 0.5; + return Extents(-halfSize, halfSize); + } + + // World + Extents getExtentsWorld() const { + Extents local = getExtentsLocal(); + return local.toWorldSpace(getCoordinateFrame()); + } + + const Extents& getFastFuzzyExtentsNoCompute() { + RBXASSERT_VERY_FAST(computeFuzzyExtents() == fuzzyExtents); + return fuzzyExtents; + } + + const Extents& getFastFuzzyExtents(); + + static float squaredDistance(const Primitive& p0, const Primitive& p1) + { + return (p0.getCoordinateFrame().translation - p1.getCoordinateFrame().translation).squaredMagnitude(); + } + + static bool aaBoxCollide(Primitive& p0, Primitive& p1) + { + return ( Extents::overlapsOrTouches( p0.getFastFuzzyExtents(), + p1.getFastFuzzyExtents()) ); + } + /////////////////////////////////////////////////////////////// + + bool hitTest(const RbxRay& worldRay, Vector3& worldHitPoint, Vector3& surfaceNormal); + + Face getFaceInObject(NormalId objectFace) const; + Face getFaceInWorld(NormalId objectFace); + + CoordinateFrame getFaceCoordInObject(NormalId objectFace) const; + + void setSurfaceType(NormalId id, SurfaceType newSurfaceType); + SurfaceType getSurfaceType(NormalId id) const {return surfaceType[id];} + + void setSurfaceData(NormalId id, const SurfaceData& newSurfaceData); + SurfaceData getSurfaceData(NormalId id) { + return surfaceData ? surfaceData[id] : SurfaceData::empty(); + } + const SurfaceData& getConstSurfaceData(NormalId id) const { + return surfaceData ? surfaceData[id] : SurfaceData::empty(); + } + + bool isGeometryOrthogonal( void ) const; + + bool computeIsGrounded( void ) const; + + + // JointK and Friction and Elasticity + float getJointK(); + + ///////////////////////////////////// + // Global Primitive Stuff + static float defaultElasticity() {return 0.75;} + static float defaultFriction() {return 0.0;} + + private: + class RigidJoint* getFirstRigidAt(Joint* start); + + public: + /////////////////////////////////////////////////////////////// + // + // Creating and breaking joints + + static void insertEdge(Edge* e); + static void removeEdge(Edge* e); + + bool hasAutoJoints() const; + + bool hasEdge() {return ((joints.size() >0) || (contacts.size() > 0));} + int getNumEdges() const {return joints.size() + contacts.size();} + Edge* getFirstEdge() const; + Edge* getNextEdge(Edge* e) const; + + int getNumJoints() const {return joints.size();} + Joint* getFirstJoint(); + Joint* getNextJoint(Joint* prev); + const Joint* getConstFirstJoint() const; + const Joint* getConstNextJoint(const Joint* prev) const; + Joint* getJoint(int id); + const Joint* getConstJoint(int id) const; + Primitive* getJointOther(int id) {return joints.getOther(id);} + + int getNumContacts() const {return contacts.size();} + + Contact* getFirstContact(); + static const bool hasGetFirstContact = true; // this is to simulate __if_exists(getFirstContact) EL + + Contact* getNextContact(Contact* prev); + Contact* getContact(int id); + Primitive* getContactOther(int id) {return contacts.getOther(id);} + + RigidJoint* getFirstRigid(); + RigidJoint* getNextRigid(RigidJoint* prev); + + static Joint* getJoint(Primitive* p0, Primitive* p1, int index = 0); + static Contact* getContact(Primitive* p0, Primitive* p1); + + static Primitive* downstreamPrimitive(Joint* j); + + ///////////////////////////////////////////////////////////////// + // SpanningNode + private: + SpanningEdge* nextSpanningEdgeFromJoint(Joint* j); + + /*override*/ SpanningEdge* getFirstSpanningEdge(); + /*override*/ SpanningEdge* getNextSpanningEdge(SpanningEdge* edge); + + private: + World* world; + Geometry* geometry; + Body* body; + IMoving* myOwner; // forward declared outside of engine + + EdgeList contacts; + EdgeList joints; + + SystemAddress networkOwner; + + Guid guid; // used for tree stuff + + unsigned int sortSize; // cached size value used for joint sorting + + int worldIndex; // For fast removal from the world primitives list + + Extents fuzzyExtents; + unsigned int fuzzyExtentsStateId; + + float specificGravity; + float jointK; + + float friction; + float elasticity; + + boost::flyweight customPhysicalProperties; + + CompactEnum material; + + // FIXED == (anchored || dragging); + bool dragging; // replicated + bool anchoredProperty; // replicated + bool preventCollide; // if dragging -> no collide + + bool networkIsSleeping; + bool jointKDirty; + + CompactEnum networkOwnershipRule; + + CompactEnum engineType; + CompactEnum sizeMultiplier; // tree stuff - overrides size/guid + + CompactEnum surfaceType[6]; // for joints.... + SurfaceData* surfaceData; + }; + +} diff --git a/App/v8world/PrismMesh.h b/App/v8world/PrismMesh.h new file mode 100644 index 0000000..c97db7a --- /dev/null +++ b/App/v8world/PrismMesh.h @@ -0,0 +1,40 @@ +#pragma once + +/* + Utility class - holds Prism Meshes of same size, and parametric shape for use by Geometry Pool. +*/ + +#include "Util/Memory.h" +#include "V8World/Mesh.h" + + +namespace RBX { + + namespace POLY { + + class PrismMesh : public Allocator + { + private: + Mesh mesh; + int NumSides; + int NumSlices; + Vector3 LocalCofM; + + public: + PrismMesh(const Vector3_2Ints& params) + { + // the zero sides and slices will cause immediate bail out of mesh builder for speed. + LocalCofM = Vector3::zero(); + mesh.makePrism(params, LocalCofM); + } + const Mesh* getMesh() const {return &mesh;} + void SetNumSides( int num ) {NumSides = num;} + void SetNumSlices( int num ) {NumSlices = num;} + const Vector3& GetLocalCofMFromMesh() const { return LocalCofM; } + }; + + + + } // namespace POLY + +} // namespace RBX diff --git a/App/v8world/PrismPoly.h b/App/v8world/PrismPoly.h new file mode 100644 index 0000000..77fe59e --- /dev/null +++ b/App/v8world/PrismPoly.h @@ -0,0 +1,44 @@ +#pragma once + +#include "V8World/Poly.h" +#include "V8World/GeometryPool.h" +#include "V8World/PrismMesh.h" +#include "V8World/BlockMesh.h" + +namespace RBX { + + class PrismPoly : public Poly { + private: + typedef GeometryPool PrismMeshPool; + PrismMeshPool::Token prismMesh; + + int numSides; + int numSlices; + + void setNumSides( int num ); + void setNumSlices( int num ); + /*override*/ bool isGeometryOrthogonal( void ) const { return false; } + + protected: + // Geometry Overrides + /*override*/ virtual GeometryType getGeometryType() const {return GEOMETRY_PRISM;} + /*override*/ void setGeometryParameter(const std::string& parameter, int value); + /*override*/ int getGeometryParameter(const std::string& parameter) const; + + /*override*/ Matrix3 getMoment(float mass) const; + /*override*/ Vector3 getCofmOffset() const; + /*override*/ CoordinateFrame getSurfaceCoordInBody( const size_t surfaceId ) const; + /*override*/ size_t getFaceFromLegacyNormalId( const NormalId nId ) const; + + // Poly Overrides + /*override*/ void buildMesh(); + + public: + PrismPoly() : numSides(0), numSlices(0) + {} + + /*override*/ bool setUpBulletCollisionData(void) { return false; } + + }; + +} // namespace diff --git a/App/v8world/PyramidMesh.h b/App/v8world/PyramidMesh.h new file mode 100644 index 0000000..22c712a --- /dev/null +++ b/App/v8world/PyramidMesh.h @@ -0,0 +1,40 @@ +#pragma once + +/* + Utility class - holds Pyramid Meshes of same size, and parametric shape for use by Geometry Pool. +*/ + +#include "Util/Memory.h" +#include "V8World/Mesh.h" + + +namespace RBX { + + namespace POLY { + + class PyramidMesh : public Allocator + { + private: + Mesh mesh; + int NumSides; + int NumSlices; + Vector3 LocalCofM; + + public: + PyramidMesh(const Vector3_2Ints& params) + { + // the zero sides and slices will cause immediate bail out of mesh builder for speed. + LocalCofM = Vector3::zero(); + mesh.makePyramid(params, LocalCofM); + } + const Mesh* getMesh() const {return &mesh;} + void SetNumSides( int num ) {NumSides = num;} + void SetNumSlices( int num ) {NumSlices = num;} + const Vector3& GetLocalCofMFromMesh() const { return LocalCofM; } + }; + + + + } // namespace POLY + +} // namespace RBX diff --git a/App/v8world/PyramidPoly.h b/App/v8world/PyramidPoly.h new file mode 100644 index 0000000..dde4a0d --- /dev/null +++ b/App/v8world/PyramidPoly.h @@ -0,0 +1,43 @@ +#pragma once + +#include "V8World/Poly.h" +#include "V8World/GeometryPool.h" +#include "V8World/PyramidMesh.h" +#include "V8World/BlockMesh.h" + +namespace RBX { + + class PyramidPoly : public Poly { + private: + typedef GeometryPool PyramidMeshPool; + PyramidMeshPool::Token pyramidMesh; + + int numSides; + int numSlices; + + void setNumSides( int num ); + void setNumSlices( int num ); + /*override*/ bool isGeometryOrthogonal( void ) const { return false; } + + protected: + // Geometry Overrides + /*override*/ GeometryType getGeometryType() const {return GEOMETRY_PYRAMID;} + /*override*/ void setGeometryParameter(const std::string& parameter, int value); + /*override*/ int getGeometryParameter(const std::string& parameter) const; + + /*override*/ Matrix3 getMoment(float mass) const; + /*override*/ Vector3 getCofmOffset() const; + /*override*/ CoordinateFrame getSurfaceCoordInBody( const size_t surfaceId ) const; + size_t getFaceFromLegacyNormalId( const NormalId nId ) const; + + // Poly Overrides + /*override*/ void buildMesh(); + + public: + PyramidPoly() : numSides(0), numSlices(0) + {} + + /*override*/ bool setUpBulletCollisionData(void) { return false; } + }; + +} // namespace diff --git a/App/v8world/RightAngleRampMesh.h b/App/v8world/RightAngleRampMesh.h new file mode 100644 index 0000000..1ccbb23 --- /dev/null +++ b/App/v8world/RightAngleRampMesh.h @@ -0,0 +1,31 @@ +#pragma once + +/* + Utility class - holds RightAngleRamp Meshes of same size for use by Geometry Pool. +*/ + +#include "Util/Memory.h" +#include "V8World/Mesh.h" + + +namespace RBX { + + namespace POLY { + + class RightAngleRampMesh : public Allocator + { + private: + Mesh mesh; + Vector3 LocalCofM; + + public: + RightAngleRampMesh(const Vector3& size) + { + mesh.makeRightAngleRamp(size, LocalCofM); + } + const Mesh* getMesh() const {return &mesh;} + const Vector3& GetLocalCofMFromMesh() const { return LocalCofM; } + }; + + } // namespace POLY +} // namespace RBX \ No newline at end of file diff --git a/App/v8world/RightAngleRampPoly.h b/App/v8world/RightAngleRampPoly.h new file mode 100644 index 0000000..15ef306 --- /dev/null +++ b/App/v8world/RightAngleRampPoly.h @@ -0,0 +1,32 @@ +#pragma once + +#include "V8World/Poly.h" +#include "V8World/GeometryPool.h" +#include "V8World/RightAngleRampMesh.h" +#include "V8World/BlockMesh.h" + +namespace RBX { + + class RightAngleRampPoly : public Poly { + public: + typedef GeometryPool RightAngleRampMeshPool; + + /*override*/ Matrix3 getMoment(float mass) const; + /*override*/ Vector3 getCofmOffset() const; + /*override*/ bool isGeometryOrthogonal( void ) const { return false; } + /*override*/ bool setUpBulletCollisionData(void) { return false; } + + private: + RightAngleRampMeshPool::Token aRightAngleRampMesh; + + protected: + // Geometry Overrides + /*override*/ virtual GeometryType getGeometryType() const {return GEOMETRY_RIGHTANGLERAMP;} + + // Poly Overrides + /*override*/ void buildMesh(); + /*override*/ size_t getFaceFromLegacyNormalId( const NormalId nId ) const; + + }; + +} // namespace diff --git a/App/v8world/RigidJoint.h b/App/v8world/RigidJoint.h new file mode 100644 index 0000000..751cde6 --- /dev/null +++ b/App/v8world/RigidJoint.h @@ -0,0 +1,55 @@ +#pragma once + +#include "V8World/Joint.h" + + +namespace RBX { + + class RigidJoint : public Joint + { + private: + /////////////////////////////////////////////////// + // Joint + /*override*/ virtual JointType getJointType() const {RBXASSERT(0); return Joint::NO_JOINT;} + /*override*/ virtual bool isBroken() const {return false;} + + /////////////////////////////////////////////////// + // KinematicJoint + // TODO: This assumes two function (one virtual) calls are way better than a dynamic cast?... + static bool jointIsRigid(Joint* j) { + JointType jt = j->getJointType(); + return ((jt == Joint::WELD_JOINT) || (jt == Joint::SNAP_JOINT) || (jt == Joint::MANUAL_WELD_JOINT)); + } + + protected: + static void faceIdToCoords( + Primitive* p0, + Primitive* p1, + NormalId nId0, + NormalId nId1, + CoordinateFrame& c0, + CoordinateFrame& c1); + + public: + RigidJoint() + {} + + RigidJoint( + Primitive* prim0, + Primitive* prim1, + const CoordinateFrame& c0, + const CoordinateFrame &c1) + : Joint(prim0, prim1, c0, c1) + {} + + ~RigidJoint() {} + + /*override*/ bool isAligned(); + + /*override*/ CoordinateFrame align(Primitive* pMove, Primitive* pStay); + + CoordinateFrame getChildInParent(Primitive* parent, Primitive* child); + }; + +} // namespace + diff --git a/App/v8world/RotateJoint.h b/App/v8world/RotateJoint.h new file mode 100644 index 0000000..ce1725e --- /dev/null +++ b/App/v8world/RotateJoint.h @@ -0,0 +1,173 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/MultiJoint.h" +#include "Util/NormalId.h" + +namespace RBX { + + class RotateConnector; + class ConstraintAlign2Axes; + class ConstraintBallInSocket; + class ConstraintAngularVelocity; + class Constraint; + + class RotateJoint : public MultiJoint + { + private: + typedef MultiJoint Super; + static RotateJoint* surfaceTypeToJoint( + SurfaceType surfaceType, + Primitive* axlePrim, + Primitive* holePrim, + const CoordinateFrame& c0, + const CoordinateFrame& c1); + + + void update(); + + protected: + typedef enum {AXLE_ID = 0, HOLE_ID} AxleHoleId; + + // Edge + /*override*/ void putInKernel(Kernel* kernel); + /*override*/ void removeFromKernel(); + /*override*/ JointType getJointType() const {return Joint::ROTATE_JOINT;} + + void getPrimitivesTorqueArmLength(float& axleArmLength, float& holeArmLength); + + ConstraintAlign2Axes* align2Axes; + ConstraintBallInSocket* ballInSocket; + + public: + RotateJoint(); + + RotateJoint( + Primitive* axlePrim, + Primitive* holePrim, + const CoordinateFrame& c0, + const CoordinateFrame& c1); + + virtual ~RotateJoint(); + + static RotateJoint* canBuildJoint( + Primitive* p0, + Primitive* p1, + NormalId nId0, + NormalId nId1); + + Primitive* getAxlePrim() {return getPrimitive(AXLE_ID);} + Primitive* getHolePrim() {return getPrimitive(HOLE_ID);} + + NormalId getAxleId() {return getNormalId(AXLE_ID);} + NormalId getHoleId() {return getNormalId(HOLE_ID);} + + Vector3 getAxleWorldDirection(); + + float getAxleVelocity(); + }; + + class DynamicRotateJoint : public RotateJoint + { + private: + typedef RotateJoint Super; + /*override*/ bool canStepWorld() const {return true;} + /*override*/ bool canStepUi() const {return true;} + + /*override*/ bool stepUi(double distributedGameTime); + /*override*/ void setPhysics(); // occurs after networking read; + + + float getChannelValue(double distributedGameTime); + + protected: + // Edge + /*override*/ void putInKernel(Kernel* kernel); + /*override*/ void removeFromKernel(); + + float baseAngle; // what is the initial assembled rotation angle + RotateConnector* rotateConnector; // here when in kernel + float uiValue; + + public: + DynamicRotateJoint() : uiValue(0.0f), rotateConnector(NULL) + {} + + DynamicRotateJoint( + Primitive* axlePrim, + Primitive* holePrim, + const CoordinateFrame& c0, + const CoordinateFrame& c1, + float baseAngle); + + ~DynamicRotateJoint(); + + float getBaseAngle() const { + return baseAngle; + } + + float getTorqueArmLength(); + + void setBaseAngle(float value); + }; + + class RotatePJoint : public DynamicRotateJoint + { + private: + // Joint + /*override*/ JointType getJointType() const {return Joint::ROTATE_P_JOINT;} + + /*override*/ void stepWorld(); + + /*override*/ void putInKernel(Kernel* kernel); + /*override*/ void removeFromKernel(); + + float currentAngle; + ConstraintAlign2Axes* alignmentConstraint; + public: + RotatePJoint(): currentAngle( 0.0f ), alignmentConstraint(NULL) + {} + + RotatePJoint( + Primitive* axlePrim, + Primitive* holePrim, + const CoordinateFrame& c0, + const CoordinateFrame& c1, + float baseAngle) + : DynamicRotateJoint(axlePrim, holePrim, c0, c1, baseAngle), currentAngle( 0.0f ), alignmentConstraint(NULL) + {} + + ~RotatePJoint(); + }; + + class RotateVJoint : public DynamicRotateJoint + { + private: + // Joint + /*override*/ JointType getJointType() const {return Joint::ROTATE_V_JOINT;} + + /*override*/ void stepWorld(); + + /*override*/ void putInKernel(Kernel* kernel); + /*override*/ void removeFromKernel(); + + ConstraintAngularVelocity* angularVelocityConstraint; + + public: + RotateVJoint(): angularVelocityConstraint( NULL ) + {} + + RotateVJoint( + Primitive* axlePrim, + Primitive* holePrim, + const CoordinateFrame& c0, + const CoordinateFrame& c1, + float baseAngle); + + ~RotateVJoint(); + }; + + + +} // namespace diff --git a/App/v8world/SendPhysics.h b/App/v8world/SendPhysics.h new file mode 100644 index 0000000..01fe909 --- /dev/null +++ b/App/v8world/SendPhysics.h @@ -0,0 +1,97 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" +#include "V8World/SimJob.h" +#include "rbx/signal.h" +#include "Util/ConcurrencyValidator.h" +#include "rbx/threadsafe.h" + + +namespace RBX { + + class Edge; + class Kernel; + class Assembly; + + class SendPhysics + { + private: + SimJobList simJobs; + + ConcurrencyValidator concurrencyValidator; + + void buildSimJob(SimJob* job); + void destroySimJob(SimJob* job); + mutable rbx::spin_mutex changeTrackerMutex; + + void setTrackerSimJob(SimJobTracker& tracker, SimJob* simJob) const + { + rbx::spin_mutex::scoped_lock lock(changeTrackerMutex); + tracker.setSimJob(simJob); + } + + public: + rbx::signal assemblyPhysicsOnSignal; + rbx::signal assemblyPhysicsOffSignal; + + SimJob* nextSimJob(SimJob* current) + { + RBXASSERT(!simJobs.empty()); + SimJobList::iterator iter = simJobs.iterator_to(*current); + ++iter; + return (iter == simJobs.end()) ? &simJobs.front() : &*iter; + } + + template + int reportSimJobs(Callback& callback, SimJobTracker& tracker, const SimJob* ignore, int numToReport = -1) + { + int reported = 0; + + ReadOnlyValidator readOnlyValidator(concurrencyValidator); + { + if (simJobs.empty()) { + return 0; + } + + if (!tracker.tracking()) { + setTrackerSimJob(tracker, &simJobs.front()); + } + + SimJob* simJob = tracker.getSimJob(); + int num = numToReport; + if (num == -1) + num = simJobs.size(); + + while (reported < num) + { + SimJob* current = simJob; + ++reported; + simJob = nextSimJob(simJob); + + if (current != ignore) + { + if (!callback(*current)) // returns true if wants another sample (m is always used) + { + break; + } + } + } + setTrackerSimJob(tracker, simJob); + } + + return reported; + } + + SendPhysics(); + + ~SendPhysics(); + + int getNumSimJobs() { return simJobs.size(); }; + + void onMovingAssemblyRootAdded(Assembly* assembly); + void onMovingAssemblyRootRemoving(Assembly* assembly); + }; + +} // namespace diff --git a/App/v8world/SimJob.h b/App/v8world/SimJob.h new file mode 100644 index 0000000..0d78a36 --- /dev/null +++ b/App/v8world/SimJob.h @@ -0,0 +1,67 @@ +/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "boost/utility.hpp" + +#include +#include +#include "boost/intrusive/list.hpp" + +namespace RBX { + + class Primitive; + class Assembly; + class SimJob; + + typedef boost::intrusive::list_base_hook< boost::intrusive::tag > SimJobHook; + typedef boost::intrusive::list > SimJobList; + + class SimJobTracker + { + private: + SimJob* simJob; + + bool containedBy(SimJob* s); + void stopTracking(); + public: + SimJobTracker() : simJob(NULL) {} + + ~SimJobTracker() { + stopTracking(); + } + + bool tracking(); + + void setSimJob(SimJob* s); + + SimJob* getSimJob(); + + static void transferTrackers(SimJob* from, SimJob* to); + }; + + class SimJob + : public boost::noncopyable + , public SimJobHook + { + friend class SimJobTracker; + + private: + std::vector trackers; // this will usually be empty, or have one tracker + Assembly* assembly; + + public: + int useCount; + + SimJob(Assembly* _assembly); + + ~SimJob(); + + Assembly* getAssembly() {return assembly;} + const Assembly* getConstAssembly() const {return assembly;} + + static SimJob* getSimJobFromPrimitive(Primitive* primitive); + static const SimJob* getConstSimJobFromPrimitive(const Primitive* primitive); + }; + +} // namespace diff --git a/App/v8world/SimulateStage.h b/App/v8world/SimulateStage.h new file mode 100644 index 0000000..d7bd2ac --- /dev/null +++ b/App/v8world/SimulateStage.h @@ -0,0 +1,63 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ +// Note - used to be called "SimJobStage.h" + +#pragma once + +#include "V8World/IWorldStage.h" +#include "V8World/Assembly.h" +#include +#include "boost/intrusive/list.hpp" +#include + +namespace RBX { + + class SimulateStage : public IWorldStage + { + public: + typedef boost::intrusive::list > Assemblies; + + private: +#if 0 + typedef boost::unordered_map AssemblyMap; +#else + typedef std::map AssemblyMap; +#endif + AssemblyMap movingAssemblyRoots; + + Assemblies movingDynamicAssemblies; + Assemblies realTimeAssemblies; + + bool validateEdge(Edge* e); + + void putFirstMovingRootInSendPhysics(Assembly* a); + void removeLastMovingRootFromSendPhysics(Assembly* a); + bool removeFromSendPhysics(Assembly* a); + + public: + SimulateStage(IStage* upstream, World* world); + + ~SimulateStage(); + + /*override*/ IStage::StageType getStageType() const {return IStage::SIMULATE_STAGE;} + + /*override*/ void onEdgeAdded(Edge* e); + /*override*/ void onEdgeRemoving(Edge* e); + + void onAssemblyAdded(Assembly* assembly); + void onAssemblyRemoving(Assembly* assembly); + + int getMovingDynamicAssembliesSize() { return movingDynamicAssemblies.size(); } + Assemblies::iterator getMovingDynamicAssembliesBegin() { + return movingDynamicAssemblies.begin(); + } + Assemblies::iterator getMovingDynamicAssembliesEnd() { + return movingDynamicAssemblies.end(); + } + Assemblies::iterator getRealTimeAssembliesBegin() { + return realTimeAssemblies.begin(); + } + Assemblies::iterator getRealtimeAssembliesEnd() { + return realTimeAssemblies.end(); + } + }; +} // namespace diff --git a/App/v8world/SleepStage.h b/App/v8world/SleepStage.h new file mode 100644 index 0000000..1af2429 --- /dev/null +++ b/App/v8world/SleepStage.h @@ -0,0 +1,169 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" +#include "V8World/Enum.h" +#include "V8World/Contact.h" +#include "Util/IndexArray.h" +#include "Util/G3DCore.h" +#include "boost/scoped_ptr.hpp" +#include "Util/RunningAverage.h" +#include +#include + + +namespace RBX { + + class Assembly; + class Edge; + class Joint; + class Contact; + class Kernel; + class SleepStage; + + namespace Profiling + { + class CodeProfiler; + } + + + class SleepStage : public IWorldStage { + public: + typedef std::set AssemblySet; + typedef std::set JointSet; + private: + typedef IWorldStage Super; + // utility - prevent extra allocs on resize + std::vector toDeep; + std::vector toWake; + std::vector toSleepingChecking; + std::vector toSleeping; + std::vector toStepping; + std::vector toContacting; + std::vector toContactingSleeping; + std::vector toSleepingJoint; + + int numContactsInStage; + int numContactsInKernel; + bool throttling; + bool debugReentrant; + int longStepId; + + typedef AssemblySet::iterator AssemblySetIt; + typedef AssemblySet::const_iterator CAssemblySetIt; + typedef IndexArray ContactList; + typedef ContactList ContactLists[Sim::NUM_THROTTLE_TYPE]; + + // defining objects + + AssemblySet recursiveWakePending; // only on impact... + AssemblySet wakePending; + AssemblySet awake; + AssemblySet sleepingChecking; // Edges that are awake + AssemblySet sleepingDeeply; // no Edges that are awake + AssemblySet removing; + ContactLists steppingContacts; + ContactLists touchingContacts; + JointSet steppingJoints; + + // Main Stepping functions + int recursivePassId; + bool externalRecursiveWake; + + void stepAssembliesRecursiveWakePending(); + + void stepAssembliesWakePending(); + + void doContacts(ContactLists& contactLists); + + void stepContacts(ContactList& contactList); + + void stepJoints(); + + void stepAssembliesAwake(); + void stepAssembliesSleepingChecking(); + + //////////////////////////////////////////////////////// + // Supporting Stepping functions + // + static float highVelocityContact(); + + void wakeAssemblies(AssemblySet& wakeSet, int maxDepth, Sim::AssemblyState checkState); + void traverse(Assembly* assembly, std::deque& aDeque, int maxDepth); + + void wakeEdge(Edge* e); + + Sim::EdgeState computeContactState(bool assembliesMoving, bool inContact, bool canCollide, bool wasTouching); + + bool highVelocityNewTouch(Contact* c); + + void wakeEvent(Edge* e); + void recursiveWakeEvent(Contact* c); + void wakeEvent(Assembly* a); + void recursiveWakeEvent(Assembly* a); + + void changeContactState(const std::vector& contacts, Sim::EdgeState newState); + void changeJointState(const std::vector& joints, Sim::EdgeState newState); + void changeAssemblyState(const std::vector& assemblies, Sim::AssemblyState newState); + + void changeContactState(Contact* c, Sim::EdgeState newState); + void changeJointState(Joint* j, Sim::EdgeState newState); + void changeAssemblyState(Assembly* a, Sim::AssemblyState newState); + + AssemblySet& stateToSet(Sim::AssemblyState state); + + bool edgeIsAwake(Edge* e); + bool isAffecting(Edge* e); + + bool atLeastOneAssemblyMoving(Assembly* a0, Assembly* a1); + + ///////////////////////////////////////////////////////////////// + // + // Assembly functions + + bool shouldSleep(Assembly* a); + bool preventNeighborSleep(Assembly* a); + + Sim::AssemblyState computeStateFromNeighbors(Assembly* a); + + bool forceNeighborAwake(Assembly* a); + bool movingTooMuchToSleep(Assembly* a); + + bool validate(); + bool validateJoints(); + public: + SleepStage(IStage* upstream, World* world); + + ~SleepStage(); + + + /////////////////////////////////////////// + // IStage + /*override*/ IStage::StageType getStageType() const {return IStage::SLEEP_STAGE;} + + /*override*/ int getMetric(IWorldStage::MetricType metricType); + + /*override*/ void onEdgeAdded(Edge* e); + /*override*/ void onEdgeRemoving(Edge* e); + + void stepSleepStage(int worldStepId, int uiStepId, bool _throttling); + + ///////////////////////////////////////////// + // From Upstream Collision Stage, World + void onAssemblyAdded(Assembly* a); + void onAssemblyRemoving(Assembly* a); + void onExternalTickleAssembly(Assembly* a, bool recursive); + + int numTouchingContacts(); + const AssemblySet& getAwakeAssemblies() const {return awake;} + + /////////////////////////////////////////// + // Profiler + boost::scoped_ptr profilingCollision; + boost::scoped_ptr profilingJointSleep; + boost::scoped_ptr profilingWake; + boost::scoped_ptr profilingSleep; + }; +} // namespace + diff --git a/App/v8world/SmoothClusterGeometry.h b/App/v8world/SmoothClusterGeometry.h new file mode 100644 index 0000000..b54c99d --- /dev/null +++ b/App/v8world/SmoothClusterGeometry.h @@ -0,0 +1,78 @@ +#pragma once + +#include "V8World/Geometry.h" +#include "V8World/Primitive.h" +#include "V8World/TerrainPartition.h" +#include "Util/PartMaterial.h" + +class btConvexHullShape; +struct btDbvt; + +namespace RBX { + + namespace Voxel2 { class Grid; } + + class SmoothClusterGeometry: public Geometry + { + public: + struct ChunkMesh; + + SmoothClusterGeometry(Primitive* p); + ~SmoothClusterGeometry(); + + // Geometry overrides + GeometryType getGeometryType() const override; + CollideType getCollideType() const override; + float getRadius() const override; + size_t closestSurfaceToPoint(const Vector3& pointInBody) const override; + Plane getPlaneFromSurface(const size_t surfaceId) const override; + CoordinateFrame getSurfaceCoordInBody(const size_t surfaceId) const override; + Vector3 getSurfaceNormalInBody(const size_t surfaceId) const override; + size_t getMostAlignedSurface(const Vector3& vecInWorld, const G3D::Matrix3& objectR) const override; + int getNumSurfaces() const override; + Vector3 getSurfaceVertInBody(const size_t surfaceId, const int vertId) const override; + int getNumVertsInSurface(const size_t surfaceId) const override; + bool vertOverlapsFace(const Vector3& pointInBody, const size_t surfaceId) const override; + bool findTouchingSurfacesConvex(const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId) const override; + bool FacesOverlapped(const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol) const override; + bool FaceVerticesOverlapped(const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol) const override; + bool FaceEdgesOverlapped(const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol) const override; + bool hitTest(const RbxRay& rayInMe, Vector3& localHitPoint, Vector3& surfaceNormal) override; + bool collidesWithGroundPlane(const CoordinateFrame& c, float yHeight) const override; + bool setUpBulletCollisionData() override; + + bool hitTestTerrain(const RbxRay& rayInMe, Vector3& localHitPoint, int& surfId, CoordinateFrame& surfCf) override; + + // Terrain specific API + bool castRay(const RbxRay& rayInMe, Vector3& localHitPoint, Vector3& surfaceNormal, unsigned char& surfaceMaterial, float maxDistance, bool ignoreWater); + bool findCellsInBoundingBox(const Vector3& min, const Vector3& max); + + void updateChunk(const Vector3int32& id); + void updateAllChunks(); + + void garbageCollectIncremental(); + + shared_ptr getBulletChunkShape(const Vector3int32& id); + TerrainPartitionSmooth* getTerrainPartition() { return partition.get(); } + + static PartMaterial getTriangleMaterial(btCollisionShape* collisionShape, unsigned int triangleIndex, const Vector3& localHitPoint); + + private: + Primitive* myPrim; + + Voxel2::Grid* grid; + + scoped_ptr partition; + + typedef boost::unordered_map ChunkMap; + ChunkMap bulletChunks; + + Vector3int32 gcChunkKeyNext; + size_t gcChunkCountLast; + size_t gcUnusedMemory; + size_t gcUnusedMemoryNext; + + btDbvt* bulletChunksTree; + }; + +} // namespace diff --git a/App/v8world/SnapJoint.h b/App/v8world/SnapJoint.h new file mode 100644 index 0000000..adb8dad --- /dev/null +++ b/App/v8world/SnapJoint.h @@ -0,0 +1,40 @@ +#pragma once + +#include "V8World/RigidJoint.h" + +namespace RBX { + + class SnapJoint : public RigidJoint + { + private: + /////////////////////////////////////////////////// + // Joint + /*override*/ virtual JointType getJointType() const {return SNAP_JOINT;} + + /////////////////////////////////////////////////// + // WeldJoint + static bool compatibleSurfaces( + Primitive* p0, + Primitive* p1, + NormalId nId0, + NormalId nId1); + + public: + SnapJoint() {} + + SnapJoint(Primitive* prim0, Primitive* prim1, const CoordinateFrame& c0, const CoordinateFrame &c1) + : RigidJoint(prim0, prim1, c0, c1) + {} + + ~SnapJoint() {} + + static SnapJoint* canBuildJoint( + Primitive* p0, + Primitive* p1, + NormalId nId0, + NormalId nId1); + }; + + +} // namespace + diff --git a/App/v8world/SpatialFilter.h b/App/v8world/SpatialFilter.h new file mode 100644 index 0000000..f3e6921 --- /dev/null +++ b/App/v8world/SpatialFilter.h @@ -0,0 +1,102 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" +#include "V8World/Assembly.h" +#include "Util/SimSendFilter.h" +#include "boost/scoped_ptr.hpp" +#include + +namespace RBX { + class Assembly; + class Mechanism; + class Joint; + class Region2; + +/* + Simulate Physics Service (Send) +Client NO --- doesn’t step --- NO --- doesn’t step -- +Server ALL If Sim +Edit / Visit Solo ALL N0 +Dphysics Client: Region or Address If Sim +Dphysics Server; Address Match (null) If Awake or Sim + (region is empty) +*/ + + class SpatialFilter : public IWorldStage { + public: + typedef std::set AssemblySet; + + bool inClientSimRegion(Assembly* a); + bool addressMatch(Assembly* a); + + static bool sendingPhase(Assembly::FilterPhase phase) {return (phase == Assembly::NoSim_Send) || (phase == Assembly::NoSim_Send_Anim);} + static bool simulatingPhase(Assembly::FilterPhase phase) {return ((phase == Assembly::Sim_SendIfSim) || (phase == Assembly::Sim_BufferZone));} + static bool noSimPhase(Assembly::FilterPhase phase) {return ((phase == Assembly::NoSim_Send) || (phase == Assembly::NoSim_Send_Anim) || (phase == Assembly::NoSim_SendIfSim) || (phase == Assembly::NoSim_SendIfSim_Anim));} + static bool animationPhase(Assembly::FilterPhase phase) {return ((phase == Assembly::NoSim_Send_Anim) || (phase == Assembly::NoSim_SendIfSim_Anim)); } + + private: + + class MoveInstructions { + public: + Assembly* a; + Assembly::FilterPhase from; + Assembly::FilterPhase to; + + MoveInstructions() : a(NULL), from(Assembly::NOT_ASSIGNED), to(Assembly::NOT_ASSIGNED) + {} + + MoveInstructions(Assembly* _a, Assembly::FilterPhase _from, Assembly::FilterPhase _to) : a(_a), from(_from), to(_to) + {} + + ~MoveInstructions() + {} + }; + + SimSendFilter filter; + + AssemblySet assemblies[Assembly::NUM_PHASES]; // no simulate assemblies and simulate assemblies + + G3D::Array toMove; + + Assembly::FilterPhase filterAssembly(Assembly* a, bool simulating, Time wakeupNow); // when simulating, this can affect the datamodel + + bool isNotClientAddress(Assembly* a); + + void changePhase(MoveInstructions& mi); + void moveInto(MoveInstructions& mi); + void removeFromPhase(Assembly* a); + void moveAll(Assembly::FilterPhase destination); + + class MechToAssemblyStage* getMechToAssemblyStage(); + + void filterAssemblies(); + + void insertPrimitiveJoints(Primitive* p); + void removePrimitiveJoints(Primitive* p); + + public: + /////////////////////////////////////////// + // IStage + SpatialFilter(IStage* upstream, World* world); + + ~SpatialFilter(); + + /*override*/ IStage::StageType getStageType() const {return IStage::SPATIAL_FILTER;} + + void filterStep(); + + void onMovingAssemblyRootAdded(Assembly* a, Time now); + void onFixedAssemblyRootAdded(Assembly* a); + + void onAssemblyRootRemoving(Assembly* a); + + SimSendFilter& getSimSendFilter() {return filter;} + + const AssemblySet& getAssemblies(Assembly::FilterPhase phase) { + RBXASSERT(phase < Assembly::NUM_PHASES); + return assemblies[phase]; + } + }; +} // namespace diff --git a/App/v8world/SpatialHashMultiRes.h b/App/v8world/SpatialHashMultiRes.h new file mode 100644 index 0000000..535fcac --- /dev/null +++ b/App/v8world/SpatialHashMultiRes.h @@ -0,0 +1,435 @@ +#pragma once + +#include "V8World/BasicSpatialHashPrimitive.h" +#include "Util/G3DCore.h" +#include "Util/Memory.h" +#include "Util/ConcurrencyValidator.h" +#include "rbx/Debug.h" +#include "rbx/object_pool.h" + +#include +#include + +namespace RBX { + + + + class World; + class Extents; + + class NodeBase + { + public: + NodeBase(short level, int hashId, const Vector3int32& gridId) + : level(level) + , hashId(hashId) + , gridId(gridId) + {}; + NodeBase() + : level(-1) + , hashId(-1) + {}; + + ~NodeBase() { + level = -2; + hashId = -2; + } + + short level; + int hashId; + Vector3int32 gridId; + + int getLevel() { + RBXASSERT(level >= -1); + return level; + } + }; + + enum enumAction + { + aRecurseTreeNode, + aVisitSingleSpatialNode, + aVisitAllSiblingsSpatialNodes + }; + + struct NodeInfo + { + + NodeInfo(NodeBase* node, enumAction action, IntersectResult intersectResult, float distance) + : node(node) + , action(action) + , intersectResult(intersectResult) + , distance(distance) + {}; + NodeBase* node; + enumAction action; + IntersectResult intersectResult; + float distance; + + // transform distance into priority (lower distance, higher priority == invert sign) + bool operator < (const NodeInfo& r) const + { + return distance > r.distance; + } + }; + + + class SpatialHashStatic { + public: + // in SpatialHashMultiRes.inl + static const int cellMinSize; + static const int maxLevelForAnchored; + inline static float hashGridSize(int level); + inline static float hashGridRecip(int level); + inline static size_t numBuckets(int level); + inline static Extents hashGridToRealExtents(int level, const Vector3int32& hashGrid); + inline static ExtentsInt32 scaleExtents(int smallLevel, int bigLevel, const ExtentsInt32& smallExtents); + inline static Vector3int32 realToHashGrid(int level, const Vector3& realPoint); + inline static Vector3 hashGridToReal(int level, const Vector3int32& hashGrid); + + // in SpatialHashMultiRes.cpp + static int getHash(int level, const Vector3int32& grid); + static void computeMinMax(const int level, const Extents& extents, Vector3int32& min, Vector3int32& max); + static void makeVisitOrder(int* offsets, const Vector3& visitDir); + + static const Extents safeExtents(const Extents& e) { + RBXASSERT(Extents(e.min(), e.max()) == e); + if (e.isNanInf()) { + RBXASSERT(0); + return Extents::zero(); + } + else { + return e; + } + } + }; + + template + class SpatialHash { + public: + struct SpaceFilter + { + virtual IntersectResult Intersects(const Extents& extents) = 0; + virtual float Distance(const Extents& extents) { return 0; }; + // return false to break iteration. + virtual bool onPrimitive(Primitive* p, IntersectResult intersectResult, float distance) = 0; + }; + + // Implement this pure virtual class in order to listen for coarse + // movement events. See registerCoarseMovementCallback for more details. + class CoarseMovementCallback { + public: + struct UpdateInfo { + enum UpdateType { + UPDATE_TYPE_Insert = 0, + UPDATE_TYPE_Change, + MAX_UPDATE_TYPES + }; + + // for Insert update type, only new{Level,SpatialExtents} are valid + // for Changed update type, both old and new info is valid + UpdateType updateType; + + int oldLevel; + ExtentsInt32 oldSpatialExtents; + int newLevel; + ExtentsInt32 newSpatialExtents; + }; + // This callback may be invoked when a part is altered from lua, + // or other sensitive areas. Implementers of this method should + // avoid modifying the parts of Primitive relating to its extents + // and/or location to avoid re-entrant behavior and unexpected + // interactions. + virtual void coarsePrimitiveMovement(Primitive* p, const UpdateInfo& info) = 0; + }; + + SpatialHash(World* world, ContactManager* contactManager, int maxCellsPerPrimitive); + ~SpatialHash(); + + // loosely sorted. + void visitPrimitivesInSpace(SpaceFilter* filter, const Vector3& visitDir); + // strict sorting of nodes according the the return value of filter->Distance(). + void visitPrimitivesInSpace(SpaceFilter* filter); + + void fastClear(); + + void onPrimitiveAdded(Primitive* p, bool addContact = true); + void onPrimitiveRemoved(Primitive* p); + void onPrimitiveExtentsChanged(Primitive* p); + void onPrimitiveAssembled(Primitive* p); + + void getPrimitivesInGrid(const Vector3int32& grid, G3D::Array& primitives); + bool getNextGrid(Vector3int32& grid, const RbxRay& unitRay, float maxDistance); + + // find all primitives that touch the same grids as touched by extents + void getPrimitivesTouchingGrids( + const Extents& extents, + const Primitive* ingore, + std::size_t maxCount, + boost::unordered_set& answer); + + void getPrimitivesTouchingGrids( + const Extents& extents, + const boost::unordered_set& ignoreSet, + std::size_t maxCount, + boost::unordered_set& answer); + + // This function iteratively processes cells that overlap with extents, which is faster on small regions + template void getPrimitivesOverlapping( const Extents& extents, Set& answer); + + // This function recursively processes cells that overlap with extents, which is faster on large regions + template void getPrimitivesOverlappingRec(const Extents& extents, Set& answer); + + // inquiry + int getNodesOut() const {return nodesOut;} + int getMaxBucket() const {return maxBucket;} + + void doStats() const; + + // Callback mechanism that allows outside systems to be notified when + // parts exibit significant movement (relative to their size). A + // movement is considered significant if it causes the part to enter + // or leave a region of space, where region size determined by + // primitive size. The size of the coarsest region is + // SpatialHashStatic::hashGridSize(MAXLEVELS - 1) so callers cannot + // depend on this callback being fired for smaller region movements, + // but in practice the majority of primitives use smaller regions. + void registerCoarseMovementCallback(CoarseMovementCallback* callback); + void unregisterCoarseMovementCallback(CoarseMovementCallback* callback); + + private: + class TreeNode; + class SpatialNode; + typedef std::pair TreeNodePair; + + // sort by the dot product of( the position of the offsets in space by the visitDir ). + struct SortOffsetByVisitDir + { + SortOffsetByVisitDir(const Vector3& visitDir) + : visitDir(visitDir) {}; + const Vector3& visitDir; + + bool operator()(const TreeNodePair& a, const TreeNodePair& b) + { + Vector3 va((float)a.first->gridId.x, (float)a.first->gridId.y, (float)a.first->gridId.z); + Vector3 vb((float)b.first->gridId.x, (float)b.first->gridId.y, (float)b.first->gridId.z); + return va.dot(visitDir) < vb.dot(visitDir); + } + }; + + struct FastClearSpatialNode + { + SpatialHash* hash; + FastClearSpatialNode(SpatialHash* h) : hash(h) {}; + void operator()(SpatialNode* node) + { + node->primitive->setOldSpatialExtents(ExtentsInt32::empty()); + hash->nodesOut--; + } + }; + + struct FastClearTreeNode + { + SpatialHash* hash; + FastClearTreeNode(SpatialHash* h) : hash(h) {}; + void operator()(TreeNode* node) + { + node->refByPrimitives = 0; + node->next = 0; + hash->numTreeNodesTotal--; + } + }; + + class TreeNode : protected NodeBase, public Allocator { + protected: + friend class SpatialHash; + friend class SpatialNode; + + unsigned short children[8]; + unsigned char childMask; + int refByPrimitives; + TreeNode *next; + + void reset() { + refByPrimitives = 0; + this->level = -1; + this->hashId = -1; + next = NULL; + childMask = 0; + for (int i=0; i<8; i++) + children[i] = 0xffff; + } + + void setChild(int i, unsigned int child) { + childMask |= (1< { + protected: + friend class SpatialHash; + friend class TreeNode; + + Primitive* primitive; // primitive associated with this node + SpatialNode* nextHashLink; // next node for this hash + #ifdef _RBX_DEBUGGING_SPATIAL_HASH + SpatialNode* nextPrimitiveLink; // next node for this primitive + SpatialNode* prevPrimitiveLink; // prior node for this primitive + #endif + TreeNode *treeNode; + + public: + SpatialNode(int l, int hashId, const Vector3int32& gridId) + : NodeBase(l, hashId, gridId) + , nextHashLink(0) + , primitive(NULL) + , treeNode(NULL) + #ifdef _RBX_DEBUGGING_SPATIAL_HASH + , nextPrimitiveLink(0) + , prevPrimitiveLink(0) + #endif + {} + + ~SpatialNode() + { + treeNode = NULL; + primitive = NULL; + nextHashLink = NULL; + } + }; + + class SpatialHashTableEntry { + public: + SpatialNode *nodes; + TreeNode *treeNodes; + }; + + protected: // default settings override on construction + static const int rootLevel; + + private: + ConcurrencyValidator concurrencyValidator; + + const int maxCellsPerPrimitive; + + int numTreeNodesTotal; + + World* world; + ContactManager* contactManager; + std::vector hashTables[MAX_LEVELS]; + int nodesOut; + int maxBucket; + G3D::Array outOfContact; // temp buffer + std::vector coarseMovementCallbacks; + + SpatialNode* newNode(int level, int hash, const Vector3int32& grid); + void returnNode(SpatialNode* node); + + TreeNode * findTreeNode( + int level, int hash, const Vector3int32 &gridCoord); + TreeNode * createTreeNode( + int level, int hash, const Vector3int32 &gridCoord); + void _retireTreeNode(TreeNode* tn); + void retireTreeNode(TreeNode* tn); + void removeTreeNodeChild(int childLevel, Vector3int32 &childGridCoord); + + bool findOtherNodesInLevel0Cell(SpatialNode* destroy); + + void checkAndReleaseContacts(Primitive *p); + + void addContactFromChildren(TreeNode *tn, Primitive *p); + + int computeLevel(const Primitive* p, const Extents& extents); + + inline bool oldExtentsOverlap(Primitive* p0, Primitive* p1); + + bool hashHasPrimitive(int level, Primitive* p, int hash, const Vector3int32& grid); + + SpatialNode* findNode(Primitive* p, const Vector3int32& grid); + void removeNodeFromHash(SpatialNode* remove); + + void insertNodeToPrimitive(SpatialNode* node, Primitive* p, const Vector3int32& grid, int hash); + + void addNode(Primitive* p, const Vector3int32& grid, bool addContact = true); + void destroyNode(SpatialNode* destroy); + + void changeMinMax( Primitive* p, + const ExtentsInt32* change, + const ExtentsInt32* oldBox, + const ExtentsInt32* newBox, + bool addContact = true); + void primitiveAdded(Primitive* p, bool addContact); + void primitiveRemoved(Primitive* p); + void primitiveExtentsChanged(Primitive* p, const Extents& extents); + + // remove these once we can confirm "boost::pool" objects work + object_pool treeNodeAllocator; + object_pool spatialNodeAllocator; + // + + inline Vector3int32 getChildGrid(const Vector3int32& grid, int offset) + { + return Vector3int32( + (grid.x << 1) + (offset & 1), // bit 0 of offset is x coord. + (grid.y << 1) + ((offset & 2) >> 1), // bit 1 of offset is y coord. + (grid.z << 1) + ((offset & 4) >> 2) // bit 2 of offset is z coord. + ); + } + + static const Extents calcNewExtents(Primitive* p); + + void visitPrimitivesInSpaceWorker(TreeNode* tn, int level, int hashId, const RBX::Vector3int32& gridId, int* visitOrder, IntersectResult intersectResult, SpaceFilter* filter, const Vector3& visitDir); + + template void getPrimitivesOverlappingRec(const Extents* extents, Set& answer, int level, int hash, const Vector3int32& gridCoord); + + private: + void setup(); + void cleanup(); + + void getPrimitivesInGrid(int level, const Vector3int32& grid, G3D::Array& primitives); + + // octree interface + TreeNode* getFirstRoot(); + TreeNode* getNextRoot(TreeNode* prevRoot); + + TreeNode* getChild(TreeNode* parent, int octant); + void getPrimitivesInTreeNode(TreeNode* treenode, G3D::Array& primitives); + + public: // for unit testing + + // DEBUGGING only + bool validateInsertNodeToPrimitive(SpatialNode* node, Primitive* p, const Vector3int32& grid, int hash); + bool validateRemoveNodeFromPrimitive(SpatialNode* node); + bool validateNodesOverlap(Primitive* p0, Primitive* p1); + bool validateTallyTreeNodes(); + bool validateTreeNodeNotHere(TreeNode* tn, int level, int hash); + bool validateContacts(Primitive* p); // debug only + bool validateNoNodesOut(); + }; + +} // namespace + +#include "v8World/SpatialHashMultiRes.inl" diff --git a/App/v8world/SpatialHashMultiRes.inl b/App/v8world/SpatialHashMultiRes.inl new file mode 100644 index 0000000..21dde99 --- /dev/null +++ b/App/v8world/SpatialHashMultiRes.inl @@ -0,0 +1,1736 @@ +#include "V8World/World.h" +#include "V8World/ContactManager.h" +#include "Util/Math.h" +#include "rbx/Debug.h" +#include "RbxAssert.h" +#include "G3D/CollisionDetection.h" + +#include + +namespace RBX { + +float SpatialHashStatic::hashGridSize(int level) {return (float)(cellMinSize << level);} +float SpatialHashStatic::hashGridRecip(int level) {return 1.0f / SpatialHashStatic::hashGridSize(level);} +size_t SpatialHashStatic::numBuckets(int level) {return 65536;} + +Vector3int32 SpatialHashStatic::realToHashGrid(int level, const Vector3& realPoint) +{ + Vector3 gridPoint = realPoint * SpatialHashStatic::hashGridRecip(level); + Vector3int32 hashGrid = Vector3int32::floor(gridPoint); // 4 grids per hash bucket + return hashGrid; +} + +ExtentsInt32 SpatialHashStatic::scaleExtents(int smallLevel, int bigLevel, const ExtentsInt32& smallExtents) +{ + RBXASSERT_SLOW(smallLevel < bigLevel); + int delta = bigLevel - smallLevel; + return smallExtents.shiftRight(delta); +} + +Extents SpatialHashStatic::hashGridToRealExtents(int level, const Vector3int32& hashGrid) +{ + Extents answer( hashGridToReal(level, hashGrid), + hashGridToReal(level, hashGrid + Vector3int32::one()) + ); + + RBXASSERT_VERY_FAST(Math::isIntegerVector3(answer.min())); + RBXASSERT_VERY_FAST(Math::isIntegerVector3(answer.max())); + + return answer; +} + + +Vector3 SpatialHashStatic::hashGridToReal(int level, const Vector3int32& hashGrid) +{ + return hashGrid.toVector3() * SpatialHashStatic::hashGridSize(level); +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#define SHP template + +// statics - templated SpatialHash +SHP const int SpatialHash::rootLevel = MAX_LEVELS-1; + +SHP SpatialHash::SpatialHash(World* world, ContactManager* contactManager, int maxCellsPerPrimitive) + : world(world) + , contactManager(contactManager) + , maxCellsPerPrimitive(maxCellsPerPrimitive) + , nodesOut(0) + , maxBucket(0) + , numTreeNodesTotal(0) +{ + setup(); +} + +SHP void SpatialHash::setup() +{ + for (int i=0; i::~SpatialHash() +{ + cleanup(); +} + +SHP void SpatialHash::cleanup() +{ + // nb: make all checks match with fastClear as well. + RBXASSERT_SPATIAL_HASH(validateTallyTreeNodes()); + RBXASSERT_SPATIAL_HASH(validateNoNodesOut()); + RBXASSERT(nodesOut == 0); +} + +SHP const Extents SpatialHash::calcNewExtents(Primitive* p) +{ + return SpatialHashStatic::safeExtents(p->getFastFuzzyExtents()); +} + + + +SHP typename SpatialHash::SpatialNode* SpatialHash::newNode(int level, int hash, const Vector3int32& grid) +{ + SpatialNode* answer; + answer = new SpatialNode(level, hash, grid); + + if (level == 0) { + answer->treeNode = NULL; + } + else { + answer->treeNode = findTreeNode(level, hash, grid); + if (! answer->treeNode) + answer->treeNode = createTreeNode(level, hash, grid); + answer->treeNode->refByPrimitives++; + + RBXASSERT(level == answer->treeNode->level); + } + + return answer; +} + + + +SHP typename SpatialHash::TreeNode *SpatialHash::findTreeNode( + int level, int hash, const Vector3int32 &gridCoord) +{ + TreeNode * tn = hashTables[level][hash].treeNodes; + while (tn) { + RBXASSERT(tn->hashId == hash); + RBXASSERT(tn->level == level); + if (tn->gridId == gridCoord) { + return tn; + } + tn = tn->next; + } + return NULL; +} + +SHP typename SpatialHash::TreeNode *SpatialHash::createTreeNode( + int level, int hash, const Vector3int32 &gridCoord) +{ + TreeNode *tn; + + tn = new TreeNode(); + + numTreeNodesTotal ++; + + tn->refByPrimitives = 0; + tn->level = level; + tn->hashId = hash; + tn->gridId = gridCoord; + + RBXASSERT_SPATIAL_HASH(validateTreeNodeNotHere(tn, level, hash)); + + tn->next = hashTables[level][hash].treeNodes; + hashTables[level][hash].treeNodes = tn; + + return tn; +} + +SHP void SpatialHash::_retireTreeNode(TreeNode* tn) { + + TreeNode** p = &hashTables[tn->level][tn->hashId].treeNodes; + while (*p != tn) { + p = &((*p)->next); + } + *p = tn->next; + tn->next = NULL; + + delete tn; + + numTreeNodesTotal --; +} + +SHP void SpatialHash::retireTreeNode(TreeNode* tn) +{ + if (--tn->refByPrimitives) { + // still being used, do nothing + return; + } + + if (tn->childMask) { + // This tree node still has children, i.e. it's part of the tree. + // Therefore, even though no SpatialNode is using this treenode (which + // in turn means this treenode corresponds to no primitives), it + // needs to be kept. + return; + } + + // this treenode's refcount is 0 AND has no children + //retire it and adjust hierarchy + + int tnlevel = tn->level; + Vector3int32 tngridId(tn->gridId); + _retireTreeNode(tn); + + // notify ancestors that this has been removed + removeTreeNodeChild(tnlevel, tngridId); +} + +SHP void SpatialHash::insertNodeToPrimitive(SpatialNode* node, + Primitive* p, + const Vector3int32& grid, + int hash) +{ + RBXASSERT(node->level == p->getSpatialNodeLevel()); + + node->primitive = p; + node->gridId = grid; + node->hashId = hash; + + RBXASSERT_SPATIAL_HASH(validateInsertNodeToPrimitive(node, p, grid, hash)); +} + + +SHP void SpatialHash::returnNode(SpatialNode* node) +{ + delete node; +} + +SHP void SpatialHash::removeNodeFromHash(SpatialNode* remove) +{ + SpatialNode** nodePtr = &hashTables[remove->getLevel()][remove->hashId].nodes; + + while (*nodePtr != remove) { + nodePtr = &((*nodePtr)->nextHashLink); + } + *nodePtr = remove->nextHashLink; +} + +SHP typename SpatialHash::SpatialNode* SpatialHash::findNode(Primitive* p, const Vector3int32& grid) +{ + const int l = p->getSpatialNodeLevel(); + + RBXASSERT_SPATIAL_HASH(static_cast(p->spatialNodes)->getLevel() == l); + RBXASSERT(SpatialHashStatic::numBuckets(l) == hashTables[l].size()); + + int hash = SpatialHashStatic::getHash(l, grid); + SpatialNode* node = hashTables[l][hash].nodes; + if (node == NULL) + return NULL; + while ((node->primitive != p) || (node->gridId != grid)) { + node = node->nextHashLink; + if (! node) { + break; + } + } + + return node; +} + + +SHP bool SpatialHash::oldExtentsOverlap(Primitive* me, Primitive* other) +{ + const int myLevel = me->getSpatialNodeLevel(); + const int otherLevel = other->getSpatialNodeLevel(); + RBXASSERT(myLevel >= 0); + RBXASSERT(otherLevel >= 0); + + bool answer = false; + + if (myLevel == otherLevel) { + answer = ExtentsInt32::overlapsOrTouches(me->getOldSpatialExtents(), other->getOldSpatialExtents()); + } else { + Primitive* big = (myLevel > otherLevel) ? me : other; + Primitive* smallPrim = (big == me) ? other : me; + ExtentsInt32 smallInBig = SpatialHashStatic::scaleExtents(smallPrim->getSpatialNodeLevel(), big->getSpatialNodeLevel(), smallPrim->getOldSpatialExtents()); + answer = ExtentsInt32::overlapsOrTouches(big->getOldSpatialExtents(), smallInBig); + } + + return answer; +} + +SHP bool SpatialHash::findOtherNodesInLevel0Cell(SpatialNode* destroy) +{ + RBXASSERT(destroy->getLevel() == 0); + Vector3int32 g = destroy->gridId; + int hash = destroy->hashId; + + SpatialNode* node_sameHash = hashTables[0][hash].nodes; + while (node_sameHash) { + if (node_sameHash->gridId == g) + { +#ifdef _DEBUG + Primitive* other = node_sameHash->primitive; + RBXASSERT(other != destroy->primitive); +#endif + return true; + } + node_sameHash = node_sameHash->nextHashLink; + } + return false; +} + + +SHP void SpatialHash::destroyNode(SpatialNode* destroy) +{ + RBXASSERT_SPATIAL_HASH(validateTallyTreeNodes()); + RBXASSERT_SPATIAL_HASH(validateRemoveNodeFromPrimitive(destroy)); + RBXASSERT_SPATIAL_HASH(destroy->getLevel() == destroy->primitive->getSpatialNodeLevel()); + + // This is insignificant for timing + removeNodeFromHash(destroy); + + // for all lower level nodes + if (destroy->getLevel() > 0) + { + RBXASSERT(destroy->treeNode); + retireTreeNode(destroy->treeNode); + } + else + { + if (!findOtherNodesInLevel0Cell(destroy)) { // this (conceptual) leaf cell is empty; maintain hierachy + removeTreeNodeChild(0, destroy->gridId); + } + } + + returnNode(destroy); + nodesOut--; + + RBXASSERT_SPATIAL_HASH(validateTallyTreeNodes()); +} + + +SHP void SpatialHash::removeTreeNodeChild(int childLevel, Vector3int32 &childGridCoord) +{ + int childHash; + RBXASSERT((childHash = SpatialHashStatic::getHash(childLevel, childGridCoord), 1)); + + for (int l=childLevel+1; l < MAX_LEVELS; l++) { + Vector3int32 g; + g.x = childGridCoord.x>>(l-childLevel); g.y = childGridCoord.y>>(l-childLevel); g.z = childGridCoord.z>>(l-childLevel); + int hash = SpatialHashStatic::getHash(l, g); + + TreeNode *tn = findTreeNode(l, hash, g); + RBXASSERT(tn); + int offset = ((childGridCoord.x>>(l-childLevel-1)) & 1)+ + (((childGridCoord.y>>(l-childLevel-1)) & 1)<<1) + (((childGridCoord.z>>(l-childLevel-1)) & 1) << 2); + RBXASSERT(tn->children[offset] == childHash); + // remove child + tn->removeChild(offset); + + // if there's still children + if (tn->refByPrimitives==0 && !tn->childMask) { + // this means the child that we've just removed is the only + // reason why this treenode existed. Now it's removed + _retireTreeNode(tn); + } else { + // if this treenode is not deleted, no need to check parent + break; + } + childHash = hash; + } +} + +SHP bool SpatialHash::hashHasPrimitive(int level, Primitive* p, int hash, const Vector3int32& grid) +{ + SpatialNode* test = hashTables[level][hash].nodes; + while (test) { + if ((test->primitive == p) && (test->gridId == grid)) { + return true; + } + test = test->nextHashLink; + } + return false; +} + +SHP void SpatialHash::addContactFromChildren(TreeNode *tn, Primitive *p) +{ + + unsigned short *children = tn->children; + Vector3int32 baseGrid; + baseGrid.x = tn->gridId.x << 1; + baseGrid.y = tn->gridId.y << 1; + baseGrid.z = tn->gridId.z << 1; + for (int i=0; i<8; i++) { + if (tn->hasChild(i)) { + Vector3int32 g; + g.x = baseGrid.x + (i & 1); + g.y = baseGrid.y + ((i & 2)>>1); + g.z = baseGrid.z + ((i & 4)>>2); + RBXASSERT(tn->level - 1 >= 0); + SpatialNode *tryNode = hashTables[ tn->level - 1 ][ children[i] ].nodes; + while (tryNode) { + Primitive* other = tryNode->primitive; + if ((other != p) && (tryNode->gridId == g )) { + if (Primitive::getContact(p, other) == NULL) + { + contactManager->onNewPair(p, other); + } + } + else { + RBXASSERT(g != tryNode->gridId); + } + tryNode = tryNode->nextHashLink; + } + + //recursive + if (tn->level > 1) { + TreeNode *c = findTreeNode(tn->level - 1, children[i], g); + RBXASSERT(c); + addContactFromChildren(c, p); + } + } + } +} + +SHP void SpatialHash::addNode(Primitive* p, const Vector3int32& grid, bool addContact) +{ + RBXASSERT(p->getSpatialNodeLevel() != -1); + + int level = p->getSpatialNodeLevel(); + + int hash = SpatialHashStatic::getHash(level, grid); + + RBXASSERT_SPATIAL_HASH(validateTallyTreeNodes()); + RBXASSERT_VERY_FAST(!hashHasPrimitive(level, p, hash, grid)); + + nodesOut++; + SpatialNode* addedNode = newNode(level, hash, grid); + +#ifdef _DEBUG + if (level > 0) { + TreeNode *tn_save = addedNode->treeNode; + RBXASSERT(addedNode->getLevel() == tn_save->level); + } +#endif + + // 1. Put in the primitive's linked list + insertNodeToPrimitive(addedNode, p, grid, hash); + + // 2. This hash's linked list of nodes + SpatialNode* tryNode = hashTables[level][hash].nodes; + addedNode->nextHashLink = tryNode; + hashTables[level][hash].nodes = addedNode; + + // 3. Cycle through the pre-existing nodes - see if any are hit + int numNodes = 1; // start with 1 - this one + + // For nodes at the same level, and higher levels nodes containing this node + Vector3int32 g = grid; + int prevHash = -1; + bool needHiearchyUpdate = true; + + for (int l=level; l < MAX_LEVELS; l++) { + if (l > level) { + g.x >>= 1; g.y >>= 1; g.z >>= 1; + hash = SpatialHashStatic::getHash(l, g); + tryNode = hashTables[l][hash].nodes; + } + + if (Primitive::hasGetFirstContact && addContact) + { + while (tryNode) { + RBXASSERT( l < MAX_LEVELS); + + numNodes++; + Primitive* other = tryNode->primitive; + if ((other != p) && (tryNode->gridId == g)) { + if (Primitive::getContact(p, other) == NULL) + { + contactManager->onNewPair(p, other); + } + } + else { + RBXASSERT(g != tryNode->gridId); + } + tryNode = tryNode->nextHashLink; + } + } + + // maintain hiearchy information + if (l > level && needHiearchyUpdate) { + TreeNode *tn = findTreeNode(l, hash, g); + if (!tn) + tn = createTreeNode(l, hash, g); + int offset = ((grid.x>>(l-level-1)) & 1)+ + (((grid.y>>(l-level-1)) & 1)<<1) + (((grid.z>>(l-level-1)) & 1) << 2); + RBXASSERT(0<=offset && offset<=7 && prevHash >= 0); + // establish child "pointer" + RBXASSERT(!tn->hasChild(offset) || tn->children[offset]==prevHash); + + if (tn->hasChild(offset)) + needHiearchyUpdate = false; // this and upper levels tree-nodes has already been set + else + tn->setChild(offset, prevHash); + + if (level > 0) + { + RBXASSERT(addedNode->treeNode && addedNode->getLevel() == addedNode->treeNode->level); + } + } + + // save immediately-lower-level hashId + prevHash = hash; + } + + // add contacts from children + if (Primitive::hasGetFirstContact && addContact && addedNode->getLevel() > 0) { + RBXASSERT(addedNode && addedNode->treeNode->level == addedNode->getLevel()); + addContactFromChildren(addedNode->treeNode, p); + } + + maxBucket = std::max(maxBucket, numNodes); + + RBXASSERT_SPATIAL_HASH(validateTallyTreeNodes()); +} + + + +SHP int SpatialHash::computeLevel(const Primitive* p, const Extents& extents) +{ + const float extra = static_cast(SpatialHashStatic::cellMinSize * 2.0); // extra buffer for thin objects + Vector3 size = extents.size() + Vector3(extra, extra, extra); + float volume = size.x * size.y * size.z; + + int maxLevel = p->requestFixed() ? SpatialHashStatic::maxLevelForAnchored : MAX_LEVELS - 1; + + float maxVolumeThisLevel = static_cast((SpatialHashStatic::cellMinSize * SpatialHashStatic::cellMinSize * SpatialHashStatic::cellMinSize)* maxCellsPerPrimitive); + + for (int answerLevel = 0; answerLevel < maxLevel; ++answerLevel) + { + if (volume < maxVolumeThisLevel) { + return answerLevel; + } + maxVolumeThisLevel *= 8; + } + return maxLevel; +} + +/* +SHP void SpatialHash::computeMinMax(const Primitive* p, const Extents& extents, Vector3int32& min, Vector3int32& max) +{ + RBXASSERT(p->getSpatialNodeLevel() != -1); + computeMinMax(p->getSpatialNodeLevel(), extents, min, max); +} +*/ + +SHP void SpatialHash::onPrimitiveAdded(Primitive* p, bool addContact) { + if( contactManager->primitiveIsExcludedFromSpatialHash(p) ) + return; + + primitiveAdded(p, addContact); + + if (!coarseMovementCallbacks.empty()) { + typename CoarseMovementCallback::UpdateInfo info; + info.updateType = CoarseMovementCallback::UpdateInfo::UPDATE_TYPE_Insert; + + info.newLevel = p->getSpatialNodeLevel(); + info.newSpatialExtents = p->getOldSpatialExtents(); + + for (size_t i = 0; i < coarseMovementCallbacks.size(); ++i) { + coarseMovementCallbacks[i]->coarsePrimitiveMovement(p, info); + } + } +} + +// Note -use FastFuzzyExtents when not in the middle of a simulation step +// Use FastFuzzyExtentsNoCompute when in a step +SHP void SpatialHash::primitiveAdded(Primitive* p, bool addContact) +{ + WriteValidator writeValidator(concurrencyValidator); + + RBXASSERT_SPATIAL_HASH(validateContacts(p)); + RBXASSERT_SPATIAL_HASH(p->spatialNodes == NULL); + RBXASSERT(p->getSpatialNodeLevel() == -1); + + Extents newExtentsFloat = calcNewExtents(p); + int level = computeLevel(p, newExtentsFloat); + + Vector3int32 newMin, newMax; + SpatialHashStatic::computeMinMax(level, newExtentsFloat, newMin, newMax); + + p->setSpatialNodeLevel(level); + ExtentsInt32 newExtents(newMin, newMax); + p->setOldSpatialExtents(newExtents); + + changeMinMax(p, &newExtents, NULL, &newExtents, addContact); + + RBXASSERT(p->getSpatialNodeLevel() == level); + RBXASSERT_SPATIAL_HASH(validateContacts(p)); +} + + +SHP void SpatialHash::changeMinMax( Primitive* p, + const ExtentsInt32* change, + const ExtentsInt32* oldBox, + const ExtentsInt32* newBox, + bool addContact) +{ + bool newEqualsChange = (newBox == change); + bool oldEqualsChange = (oldBox == change); + + for (int i = change->low.x; i <= change->high.x; ++i) { + for (int j = change->low.y; j <= change->high.y; ++j) { + for (int k = change->low.z; k <= change->high.z; ++k) { + Vector3int32 v(i, j, k); + const bool inNew = newEqualsChange || (newBox && newBox->contains(v)); + const bool inOld = oldEqualsChange || (oldBox && oldBox->contains(v)); + if (inNew && !inOld) { + addNode(p, v, addContact); // only update bucket counts when moving to avoid + } // big counts around 0,0,0 + else if (inOld && !inNew) { + SpatialNode* destroyMe = findNode(p, v); + RBXASSERT(destroyMe); + destroyNode(destroyMe); + } + } + } + } +} + +SHP void SpatialHash::fastClear() +{ + FastClearSpatialNode fastClearSpatialNode(this); + FastClearTreeNode fastClearTreeNode(this); + + for (int level = 0; level < MAX_LEVELS; level ++) + { + for (int hashId=0; hashId<(int)SpatialHashStatic::numBuckets(level); hashId++) + { + SpatialNode *sn = hashTables[level][hashId].nodes; + while (sn) + { + SpatialNode *nodeToFree = sn; + sn = nodeToFree->nextHashLink; + fastClearSpatialNode(nodeToFree); + delete nodeToFree; + } + + TreeNode *tn = hashTables[level][hashId].treeNodes; + while (tn) + { + TreeNode *nodeToFree = tn; + tn = nodeToFree->next; + RBXASSERT((fastClearTreeNode(nodeToFree), true)); // only run this if asserts are on. + delete nodeToFree; + }; + } + } + + Allocator::releaseMemory(); + Allocator::releaseMemory(); + + maxBucket = 0; + + cleanup(); + + setup(); +} + +SHP void SpatialHash::checkAndReleaseContacts(Primitive *p) +{ + if (Primitive::hasGetFirstContact ) + { + outOfContact.clear(); + + Contact * contact = p->getFirstContact(); + while (contact) { + Primitive *other = contact->otherPrimitive(p); + contact = p->getNextContact(contact); + if( contactManager->primitiveIsExcludedFromSpatialHash(other) ) + continue; + bool overlap = oldExtentsOverlap(p, other); + RBXASSERT_SPATIAL_HASH(overlap == validateNodesOverlap(p, other)); + if (!overlap) { + outOfContact.append(other); + } + } + for (int i=0; i<(int)outOfContact.size(); i++) + contactManager->releasePair(p, outOfContact[i]); + } +} + +SHP void SpatialHash::primitiveExtentsChanged(Primitive* p, const Extents& extents) +{ + WriteValidator writeValidator(concurrencyValidator); + + RBXASSERT_SPATIAL_HASH(validateContacts(p)); + RBXASSERT_SPATIAL_HASH(p->spatialNodes != NULL); + RBXASSERT(p->getSpatialNodeLevel() != -1); + + Vector3int32 newMin, newMax; + + // For now, never change a primitive's level in the hierarchy, once it + // has been determined at creation time + SpatialHashStatic::computeMinMax(p->getSpatialNodeLevel(), extents, newMin, newMax); + + if ( (newMin == p->getOldSpatialMin()) + && (newMax == p->getOldSpatialMax())) { + return; + } + + ExtentsInt32 oldBox(p->getOldSpatialExtents()); + ExtentsInt32 newBox(newMin, newMax); + p->setOldSpatialExtents(newBox); + + if (ExtentsInt32::overlapsOrTouches(oldBox, newBox)) { + ExtentsInt32 unionBox = ExtentsInt32::unionExtents(oldBox, newBox); + changeMinMax(p, &unionBox, &oldBox, &newBox); + } + else { + changeMinMax(p, &oldBox, &oldBox, NULL); + RBXASSERT_SPATIAL_HASH(p->spatialNodeCount == 0); + changeMinMax(p, &newBox, NULL, &newBox); + } + + checkAndReleaseContacts(p); + + RBXASSERT_SPATIAL_HASH(validateContacts(p)); +} + +SHP void SpatialHash::onPrimitiveRemoved(Primitive* p) { + primitiveRemoved(p); +} + +SHP void SpatialHash::primitiveRemoved(Primitive* p) +{ + WriteValidator writeValidator(concurrencyValidator); + RBXASSERT_SPATIAL_HASH(validateContacts(p)); + RBXASSERT(p->getSpatialNodeLevel() != -1 || contactManager->primitiveIsExcludedFromSpatialHash(p)); + + changeMinMax(p, &p->getOldSpatialExtents(), &p->getOldSpatialExtents(), NULL); + p->setSpatialNodeLevel(-1); + p->setOldSpatialExtents(ExtentsInt32::empty()); + RBXASSERT_SPATIAL_HASH(p->spatialNodeCount == 0); + + if (Primitive::hasGetFirstContact ) + { + outOfContact.clear(); + + for (int i = 0; i < p->getNumContacts(); i++) + outOfContact.append(p->getContactOther(i)); + + for (int i = 0; i < (int)outOfContact.size(); i++) + contactManager->releasePair(p, outOfContact[i]); + } + + RBXASSERT_SPATIAL_HASH(validateContacts(p)); +} + +// Note -use FastFuzzyExtents when not in the middle of a simulation step +// Use FastFuzzyExtentsNoCompute when in a step +SHP void SpatialHash::onPrimitiveExtentsChanged(Primitive* p) +{ + if( contactManager->primitiveIsExcludedFromSpatialHash(p) ) + return; + + contactManager->checkTerrainContact(p); + + Extents newExtentsFloat = calcNewExtents(p); + int newLevel = computeLevel(p, newExtentsFloat); + int oldLevel = p->getSpatialNodeLevel(); + int delta = newLevel - oldLevel; + ExtentsInt32 preUpdateSpatialExtents = p->getOldSpatialExtents(); + + if ((delta > 0) || (delta < -1)) // grow always, shrink only if 2 steps down + { + primitiveRemoved(p); + primitiveAdded(p, true); + } + else + { + primitiveExtentsChanged(p, newExtentsFloat); + } + + ExtentsInt32 postUpdateSpatialExtents = p->getOldSpatialExtents(); + if (!coarseMovementCallbacks.empty() && + (newLevel != oldLevel || preUpdateSpatialExtents != postUpdateSpatialExtents)) { + typename CoarseMovementCallback::UpdateInfo info; + info.updateType = CoarseMovementCallback::UpdateInfo::UPDATE_TYPE_Change; + info.oldLevel = oldLevel; + info.oldSpatialExtents = preUpdateSpatialExtents; + info.newLevel = newLevel; + info.newSpatialExtents = postUpdateSpatialExtents; + + for (size_t i = 0; i < coarseMovementCallbacks.size(); ++i) { + coarseMovementCallbacks[i]->coarsePrimitiveMovement(p, info); + } + } +} + +// Now that primitives are assembled into mechanisms we know the full topology to filter +// internal contacts. So let's query the spatial hash to create the contacts and rely on +// onNewPair() to do the filtering + +SHP void SpatialHash::onPrimitiveAssembled(Primitive* p) +{ + if( contactManager->primitiveIsExcludedFromSpatialHash(p) ) + return; + + RBXASSERT_SPATIAL_HASH(validateContacts(p)); + RBXASSERT_SPATIAL_HASH(p->spatialNodes != NULL); + RBXASSERT(p->getSpatialNodeLevel() != -1); + + WriteValidator writeValidator(concurrencyValidator); + + Extents newExtents = calcNewExtents(p); + int level = computeLevel(p, newExtents); + Vector3int32 newMin, newMax; + SpatialHashStatic::computeMinMax(level, newExtents, newMin, newMax); + + for (int i = newMin.x; i <= newMax.x; ++i) { + for (int j = newMin.y; j <= newMax.y; ++j) { + for (int k = newMin.z; k <= newMax.z; ++k) { + Vector3int32 grid(i, j, k); + SpatialNode* thisNode = findNode(p, grid); + if (!thisNode) + continue; + + int hash = SpatialHashStatic::getHash(level, grid); + + // This hash's linked list of nodes + SpatialNode* tryNode = hashTables[level][hash].nodes; + + // For nodes at the same level, and higher levels nodes containing this node + Vector3int32 g = grid; + + for (int l=level; l < MAX_LEVELS; l++) { + if (l > level) { + g.x >>= 1; g.y >>= 1; g.z >>= 1; + hash = SpatialHashStatic::getHash(l, g); + tryNode = hashTables[l][hash].nodes; + } + + while (tryNode) { + RBXASSERT( l < MAX_LEVELS); + + Primitive* other = tryNode->primitive; + if ((other != p) && (tryNode->gridId == g)) { + if (Primitive::getContact(p, other) == NULL) + { + contactManager->onNewPair(p, other); + } + } + tryNode = tryNode->nextHashLink; + } + } + + // add contacts from children + if (thisNode->getLevel() > 0) { + RBXASSERT(thisNode && thisNode->treeNode->level == thisNode->getLevel()); + addContactFromChildren(thisNode->treeNode, p); + } + } + } + } +} + + +SHP void SpatialHash::getPrimitivesTouchingGrids(const Extents& extents, + const Primitive* ignore, + std::size_t maxCount, + boost::unordered_set& answer) +{ + ReadOnlyValidator readOnlyValidator(concurrencyValidator); + + RBXASSERT(answer.size() == 0); + + + Vector3int32 min, max; + G3D::Array foundThisGrid; // prevent allocations + + for (int level=0; level 0 && answer.size() >= maxCount) + return; + } + } + } + } + } + } + RBXASSERT(foundThisGrid.size() < 200); +} + +// same as above function, but we use a set of all primitives to-be-ignored for use with an ancestor check +SHP void SpatialHash::getPrimitivesTouchingGrids(const Extents& extents, + const boost::unordered_set& ignoreSet, + std::size_t maxCount, + boost::unordered_set& answer) +{ + ReadOnlyValidator readOnlyValidator(concurrencyValidator); + + RBXASSERT(answer.size() == 0); + + + Vector3int32 min, max; + G3D::Array foundThisGrid; // prevent allocations + + for (int level=0; level 0 && answer.size() >= maxCount) + return; + } + } + } + } + } + } + } + RBXASSERT(foundThisGrid.size() < 200); +} + +SHP template void SpatialHash::getPrimitivesOverlapping(const Extents& extents, Set& answer) +{ + ReadOnlyValidator readOnlyValidator(concurrencyValidator); + + for (int level=0; levelgridId == grid) { + Primitive* p = node->primitive; + + if (skipOverlapTest || extents.overlapsOrTouches(calcNewExtents(p))) { + answer.insert(p); + } + } + node = node->nextHashLink; + } + } + } + } + } +} + +SHP template void SpatialHash::getPrimitivesOverlappingRec(const Extents& extents, Set& answer) +{ + ReadOnlyValidator readOnlyValidator(concurrencyValidator); + + // A small negative offset is added to extentsMax before computeMinMax to prevent querying extra layer of cells for perfectly aligned/sized extents + Extents adjustedExtents(extents.min(), (extents.max() - Vector3(0.01f, 0.01f, 0.01f)).max(extents.min())); + + int level = MAX_LEVELS - 1; + + Vector3int32 min, max; + SpatialHashStatic::computeMinMax(level, adjustedExtents, min, max); + + // If the tested region is contained within the extents we don't have to perform precise overlap tests + // This is very important since tests require reading Primitive memory which leads to extra cache misses. + Vector3 minReal = SpatialHashStatic::hashGridToReal(level, min); + Vector3 maxReal = SpatialHashStatic::hashGridToReal(level, max + Vector3int32(1, 1, 1)); + + bool skipOverlapTest = extents.contains(minReal) && extents.contains(maxReal); + + for (int i = min.x; i <= max.x; ++i) { + for (int j = min.y; j <= max.y; ++j) { + for (int k = min.z; k <= max.z; ++k) { + Vector3int32 grid(i, j, k); + int hash = SpatialHashStatic::getHash(level, grid); + + getPrimitivesOverlappingRec(skipOverlapTest ? NULL : &extents, answer, level, hash, grid); + } + } + } +} + +SHP template void SpatialHash::getPrimitivesOverlappingRec(const Extents* extents, Set& answer, int level, int hash, const Vector3int32& gridCoord) +{ + // Look for nodes at current level + SpatialNode* node = hashTables[level][hash].nodes; + + while (node) + { + if (node->gridId == gridCoord) + { + Primitive* p = node->primitive; + + if (!extents || extents->overlapsOrTouches(calcNewExtents(p))) + answer.insert(p); + } + + node = node->nextHashLink; + } + + // Look for nodes at the next level with smaller cells + if (level > 0) + { + TreeNode* treeNode = hashTables[level][hash].treeNodes; + + while (treeNode) + { + if (treeNode->gridId == gridCoord) + { + for (int child = 0; child < 8; ++child) + if (treeNode->hasChild(child)) + { + int childHash = treeNode->children[child]; + Vector3int32 childGrid = getChildGrid(gridCoord, child); + + getPrimitivesOverlappingRec(extents, answer, level - 1, childHash, childGrid); + } + + // Just one tree node for every cell, no need to look further + return; + } + + treeNode = treeNode->next; + } + } +} + +SHP void SpatialHash::getPrimitivesInGrid(int level, const Vector3int32& grid, G3D::Array& found) +{ + ReadOnlyValidator readOnlyValidator(concurrencyValidator); + + int hash = SpatialHashStatic::getHash(level, grid); + + SpatialNode* node = hashTables[level][hash].nodes; + + while (node) { + if (node->gridId == grid) { + RBXASSERT_IF_VALIDATING(!found.contains(node->primitive)); + found.append(node->primitive); + } + node = node->nextHashLink; + } +} + +// find primitives at all levels--the input grid coord is for level 0 +SHP void SpatialHash::getPrimitivesInGrid(const Vector3int32& grid, G3D::Array& found) +{ + RBXASSERT(found.size() == 0); + + Vector3int32 g = grid; + + for (int level = 0; level < MAX_LEVELS; + level++, g.x>>=1, g.y>>=1, g.z>>=1) { + getPrimitivesInGrid(level, g, found); + } +} + + +SHP bool SpatialHash::getNextGrid(Vector3int32& grid, + const RbxRay& unitRay, + float maxDistance) +{ + ReadOnlyValidator readOnlyValidator(concurrencyValidator); + + RBXASSERT_VERY_FAST(unitRay.direction().isUnit()); + + int low[3], high[3]; + for (int i = 0; i < 3; ++i) { + low[i] = (unitRay.direction()[i] < 0.0) ? -1 : 0; + high[i] = (unitRay.direction()[i] > 0.0) ? 1 : 0; + } + + maxDistance += SpatialHashStatic::hashGridSize(0) * 2.0f; // collision detection returns the first hit in a grid box - this could + // be farther away than the actual ultimate hit point + float maxDistanceSquared = maxDistance * maxDistance; + + for (int nz = 1; nz <= 3; ++ nz) { // number of non zeros - 1: adjacent face (6), edge (12), corner (8) + for (int i = low[0]; i <= high[0]; ++i) { + for (int j = low[1]; j <= high[1]; ++j) { + for (int k = low[2]; k <= high[2]; ++k) { + if ((std::abs(i) + std::abs(j) + std::abs(k)) == nz) { // start with this number of nonzeros + Vector3int32 offset(i, j, k); + Extents extents = SpatialHashStatic::hashGridToRealExtents(0, grid + offset); + AABox box(extents.min(), extents.max()); + Vector3 location; + bool inside; + bool result = G3D::CollisionDetection::collisionLocationForMovingPointFixedAABox( unitRay.origin(), unitRay.direction(), box, location, inside ); + if( inside || result ) + { + if ((location - unitRay.origin()).squaredMagnitude() < maxDistanceSquared) { + grid = grid + offset; + return true; + } + } + } + }}}} + return false; +} + + +SHP typename SpatialHash::TreeNode* SpatialHash::getFirstRoot() +{ + TreeNode * tn = NULL; + for(size_t id = 0; id < SpatialHashStatic::numBuckets(rootLevel) && tn == NULL; id++) + { + tn = hashTables[rootLevel][id].treeNodes; + } + return tn; +} + +SHP typename SpatialHash::TreeNode* SpatialHash::getNextRoot(TreeNode* prevRoot) +{ + if(prevRoot) + { + if(prevRoot->next) + { + return prevRoot->next; + } + else + { + // go to next non-empty hash grid + TreeNode * tn = NULL; + for(size_t id = prevRoot->hashId+1; id < SpatialHashStatic::numBuckets(rootLevel) && tn == NULL; id++) + { + tn = hashTables[rootLevel][id].treeNodes; + } + return tn; + } + } + else + { + return NULL; + } + +} + +SHP typename SpatialHash::TreeNode* SpatialHash::getChild(TreeNode* parent, int octant) +{ + RBXASSERT(parent->hasChild(octant)); + if(parent->level > 0) + { + return findTreeNode(parent->level-1, parent->children[octant], getChildGrid(parent->gridId, octant)); + } + else + { + return NULL; + } +} + +SHP void SpatialHash::getPrimitivesInTreeNode(TreeNode* tn, G3D::Array& primitives) +{ + SpatialNode* node = hashTables[tn->level][tn->hashId].nodes; + + while (node) { + if (node->gridId == tn->gridId) { + RBXASSERT_IF_VALIDATING(!primitives.contains(node->primitive)); + primitives.append(node->primitive); + } + node = node->nextHashLink; + } +} + + +SHP void SpatialHash::visitPrimitivesInSpace(SpaceFilter* filter, const Vector3& visitDir) +{ + ReadOnlyValidator readOnlyValidator(concurrencyValidator); + + int visitOrder[8]; + SpatialHashStatic::makeVisitOrder(visitOrder, visitDir); + + typedef std::pair TreeNodePair; + std::vector roots; + + // get all the root nodes, sort by visitDir. (after testing for intersect) + for(TreeNode* root = getFirstRoot(); root; root = getNextRoot(root)) + { + IntersectResult childIntersect = filter->Intersects(SpatialHashStatic::hashGridToRealExtents(root->level, root->gridId)); + + if(childIntersect == irNone) + { + continue; // no interesct at all from this node and all child nodes. + } + roots.push_back(TreeNodePair(root, childIntersect)); + } + + if(visitDir != Vector3::zero()) + { + SortOffsetByVisitDir sortPred(visitDir); + + std::sort(roots.begin(), roots.end(), sortPred); + } + + for(size_t i = 0; i < roots.size(); ++i) + { + TreeNode* tn = roots[i].first; + visitPrimitivesInSpaceWorker(tn, tn->level, tn->hashId, tn->gridId, visitOrder, roots[i].second /*childintersect*/, filter, visitDir); + } + +} + +SHP void SpatialHash::visitPrimitivesInSpace(SpaceFilter* filter) +{ + ReadOnlyValidator readOnlyValidator(concurrencyValidator); + + + + std::priority_queue nodestovisit; + + bool bContinueIterating = true; // becomes false when onPrimitives returns false + + // get all the root nodes, sort by visitDir. (after testing for intersect) + for(TreeNode* root = getFirstRoot(); root; root = getNextRoot(root)) + { + Extents extents = SpatialHashStatic::hashGridToRealExtents(root->level, root->gridId); + IntersectResult childIntersect = filter->Intersects(extents); + if(childIntersect == irNone) + { + continue; // no interesct at all from this node and all child nodes. + } + nodestovisit.push(NodeInfo(root, aRecurseTreeNode, childIntersect, filter->Distance(extents))); + } + + while(!nodestovisit.empty() && bContinueIterating) + { + NodeInfo nodeinfo(nodestovisit.top()); + nodestovisit.pop(); + NodeBase* node = nodeinfo.node; + int level = node->level; + int hashId = node->hashId; + const RBX::Vector3int32& gridId = node->gridId; + + // check the children + if(nodeinfo.action == aRecurseTreeNode) + { + // all pushed NodeBases are TreeNode if they are not level 0. + TreeNode* tn = static_cast(node); + + int childLevel = level-1; + for(int childoffset = 0; childoffset < 8; ++childoffset) + { + if(!tn->hasChild(childoffset)) + { + continue; // no child, skip. + } + + Vector3int32 childGridId = getChildGrid(tn->gridId, childoffset); + int childHashId = tn->children[childoffset]; + + IntersectResult childIntersect = irFull; + Extents childExtents = SpatialHashStatic::hashGridToRealExtents(childLevel, childGridId); + if(nodeinfo.intersectResult == irPartial) // must keep checking bounds + { + childIntersect = filter->Intersects(childExtents); + if(childIntersect == irNone) + { + continue; // no interesct at all from this node and all child nodes. + } + } + + TreeNode* childTreeNode = NULL; + if(childLevel > 0) + { + childTreeNode = findTreeNode(childLevel, childHashId, childGridId); + RBXASSERT(childTreeNode); + RBXASSERT(childTreeNode->gridId == childGridId); + RBXASSERT(childTreeNode->level == childLevel); + RBXASSERT(tn->children[childoffset] == childTreeNode->hashId); + + // we have childTreeNode; + nodestovisit.push(NodeInfo(childTreeNode, aRecurseTreeNode, childIntersect, filter->Distance(childExtents))); + } + else // since we don't have treenodes at the leaf level, we must "reach down" from the above level, instead of "recursing". + { + // no childTreeNode, must pass a spatialNode; + SpatialNode* snode = hashTables[childLevel][childHashId].nodes; + + bool extentsNeedCalc = true; + float extentsDistance = 0; + + while (snode) { + if (snode->gridId == childGridId) + { + if(extentsNeedCalc) // only calculate distance for this extents 0 or 1 times. + { + extentsDistance = filter->Distance(childExtents); + } +#ifndef PRECISE_SORTING + nodestovisit.push(NodeInfo(snode, aVisitAllSiblingsSpatialNodes, childIntersect, extentsDistance)); + break; +#else + // precise sorting. + nodestovisit.push(NodeInfo(snode, aVisitSingleSpatialNode, childIntersect, extentsDistance))); +#endif + } + snode = snode->nextHashLink; + } + + } + + } + } + + if (nodeinfo.action != aVisitSingleSpatialNode ) + { + // this treenode could have sibling spatial nodes. + SpatialNode* snode = hashTables[level][hashId].nodes; + while (snode && bContinueIterating) { + if (snode->gridId == gridId) + { +#ifndef PRECISE_SORTING + bContinueIterating = filter->onPrimitive(snode->primitive, nodeinfo.intersectResult, nodeinfo.distance); +#else + nodestovisit.push(NodeInfo(snode, aVisitSingleSpatialNode, nodeinfo.intersectResult, filter->Distance(snode->primitive->getFastFuzzyExtents()))); +#endif + } + snode = snode->nextHashLink; + } + } + else //if (nodeinfo.action == aVisitSingleSpatialNode) + { + bContinueIterating = filter->onPrimitive(static_cast(node)->primitive, nodeinfo.intersectResult, nodeinfo.distance); + } + } + +} + +SHP void SpatialHash::visitPrimitivesInSpaceWorker(TreeNode* tn, int level, int hashId, const RBX::Vector3int32& gridId, int* visitOrder, IntersectResult intersectResult, SpaceFilter* filter, const Vector3& visitDir) +{ + RBXASSERT(intersectResult == irFull || intersectResult == irPartial); + + // check the children + if(tn) + { + int childLevel = level-1; + for(int offseti = 0; offseti < 8; ++offseti) + { + int childoffset = visitOrder[offseti]; + if(!tn->hasChild(childoffset)) + { + continue; // no child, skip. + } + + Vector3int32 childGridId = getChildGrid(tn->gridId, childoffset); + int childHashId = tn->children[childoffset]; + + IntersectResult childIntersect = irFull; + if(intersectResult == irPartial) // must keep checking bounds + { + childIntersect = filter->Intersects(SpatialHashStatic::hashGridToRealExtents(childLevel, childGridId)); + if(childIntersect == irNone) + { + continue; // no interesct at all from this node and all child nodes. + } + } + + TreeNode* childTreeNode = NULL; + if(childLevel > 0) + { + childTreeNode = findTreeNode(childLevel, childHashId, childGridId); + RBXASSERT(childTreeNode); + RBXASSERT(childTreeNode->gridId == childGridId); + RBXASSERT(childTreeNode->level == childLevel); + RBXASSERT(tn->children[childoffset] == childTreeNode->hashId); + + } + + visitPrimitivesInSpaceWorker(childTreeNode, childLevel, childHashId, childGridId, visitOrder, childIntersect, filter, visitDir); + } + } + + // visit the primitives + SpatialNode* node = hashTables[level][hashId].nodes; + + bool bContinue = true; + while (node && bContinue) { + if (node->gridId == gridId) { + bContinue = filter->onPrimitive(node->primitive, intersectResult, 0); + } + + node = node->nextHashLink; + } +} + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// +// Debugging - all of these should RBXASSERT(assertingSpatialHash) +// +SHP bool SpatialHash::validateInsertNodeToPrimitive(SpatialNode* node, + Primitive* p, + const Vector3int32& grid, + int hash) +{ + RBXASSERT(assertingSpatialHash); + +#ifdef _RBX_DEBUGGING_SPATIAL_HASH + // Primitive P's linked list of nodes + SpatialNode* oldFirst = static_cast(p->spatialNodes); + p->spatialNodes = node; + + node->nextPrimitiveLink = oldFirst; + node->prevPrimitiveLink = NULL; + if (oldFirst) { + oldFirst->prevPrimitiveLink = node; + } + p->spatialNodeCount++; +#endif + return true; +} + +SHP bool SpatialHash::validateRemoveNodeFromPrimitive(SpatialNode* node) +{ + RBXASSERT(assertingSpatialHash); + +#ifdef _RBX_DEBUGGING_SPATIAL_HASH + SpatialNode* prev = node->prevPrimitiveLink; + SpatialNode* next = node->nextPrimitiveLink; + + if (next) { + next->prevPrimitiveLink = prev; + } + if (prev) { + prev->nextPrimitiveLink = next; + } + else { // !prev + node->primitive->spatialNodes = next; + } + --node->primitive->spatialNodeCount; +#endif + + return true; +} + + +SHP bool SpatialHash::validateNodesOverlap(Primitive* me, Primitive* other) +{ + RBXASSERT(assertingSpatialHash); + +#ifdef _RBX_DEBUGGING_SPATIAL_HASH + + SpatialNode* myNode = static_cast(me->spatialNodes); + SpatialNode* otherNode = static_cast(other->spatialNodes); + + if (!myNode || !otherNode) + return false; + + if (myNode->getLevel() == otherNode->getLevel()) { + while (myNode) { + SpatialNode* hashNode = hashTables[myNode->getLevel()][myNode->hashId].nodes; + while (hashNode) { + if ((hashNode->primitive == other) && (hashNode->gridId == myNode->gridId)) { + return true; + } + hashNode = hashNode->nextHashLink; + } + myNode = myNode->nextPrimitiveLink; + } + } else { + SpatialNode *lower, *higher; + Primitive *primHigher; + + if (myNode->getLevel() > otherNode->getLevel()) { + lower = otherNode; + higher = myNode; + primHigher = me; + } else { + lower = myNode; + higher = otherNode; + primHigher = other; + } + + + int levelDiff = higher->getLevel() - lower->getLevel(); + while (lower) { + Vector3int32 gridCoordHigher; + gridCoordHigher.x = (lower->gridId.x >> levelDiff); + gridCoordHigher.y = (lower->gridId.y >> levelDiff); + gridCoordHigher.z = (lower->gridId.z >> levelDiff); + + int hashHigher = getHash(higher->getLevel(), gridCoordHigher); + + SpatialNode* node = hashTables[higher->getLevel()][hashHigher].nodes; + while (node) { + if ((node->primitive == primHigher) && (node->gridId == gridCoordHigher)) { + return true; + } + node = node->nextHashLink; + } + lower = lower->nextPrimitiveLink; + } + } +#endif + + return false; +} + + +SHP bool SpatialHash::validateTallyTreeNodes() +{ + RBXASSERT(assertingSpatialHash); + + int numTreeNodesInUse=0, numTreeNodesInPool=0; + for (int level = 0; level < MAX_LEVELS; level ++) { + for (int hashId=0; hashId<(int)SpatialHashStatic::numBuckets(level); hashId++) { + TreeNode *tn = hashTables[level][hashId].treeNodes; + while (tn) { + numTreeNodesInUse ++; + RBXASSERT(tn->childMask || tn->refByPrimitives); + tn = tn->next; + } + } + } + return numTreeNodesInUse + numTreeNodesInPool == numTreeNodesTotal; +} + + +SHP bool SpatialHash::validateTreeNodeNotHere(TreeNode* tn, int level, int hash) +{ + RBXASSERT(assertingSpatialHash); + + TreeNode *p = hashTables[level][hash].treeNodes; + while (p) { + RBXASSERT(p != tn); + p = p->next; + } + return true; +} + +SHP bool SpatialHash::validateNoNodesOut() +{ + RBXASSERT(assertingSpatialHash); + + for (int l=0; l < MAX_LEVELS; l++) + { + RBXASSERT(hashTables[l].size() == SpatialHashStatic::numBuckets(l)); + for (size_t i = 0; i < SpatialHashStatic::numBuckets(l); ++i) + { + RBXASSERT(hashTables[l][i].nodes == NULL); + } + } + return true; +} + +SHP bool SpatialHash::validateContacts(Primitive* p) +{ + RBXASSERT(assertingSpatialHash); + +#ifdef _RBX_DEBUGGING_SPATIAL_HASH + + __if_exists(Primitive::getFirstContact) + { + Contact* c = p->getFirstContact(); + + // 1. For each contact, confirm there is a hash collision + while (c) { + Primitive* other = c->otherPrimitive(p); + RBXASSERT_SPATIAL_HASH(validateNodesOverlap(p, other)); + RBXASSERT_SPATIAL_HASH(validateNodesOverlap(p, other)); + c = p->getNextContact(c); + } + + // 2. For each node with other, confirm there is a contact + SpatialNode* myNode = static_cast(p->spatialNodes); + while (myNode) { + SpatialNode* hashNode = hashTables[myNode->getLevel()][myNode->hashId].nodes; + while (hashNode) { + if ((hashNode->primitive != p) && (hashNode->gridId == myNode->gridId)) { + RBXASSERT_SPATIAL_HASH(Primitive::getContact(p, hashNode->primitive)); + } + hashNode = hashNode->nextHashLink; + } + myNode = myNode->nextPrimitiveLink; + } + } +#endif + + return true; +} + + +SHP void SpatialHash::doStats() const +{ +#if 0 +bool operator<(const Vector3int32& a, const Vector3int32& b) { + for (int i = 0; i < 3; ++i) + { + if (a[i] < b[i]) { + return true; + } + else if (a[i] > b[i]) { + return false; + } + } + return false; +} + +size_t computeNumNodes(SpatialNode* node) +{ + int answer = 0; + while (node) + { + answer++; + node = node->nextHashLink; + } + return answer; +} + +size_t computeNumGrids(SpatialNode* node) +{ + std::set grids; + while (node) + { + grids.insert(node->gridId); + node = node->nextHashLink; + } + return grids.size(); +} + + // little test of hash function, not normally run + std::set num_list1; + std::set num_list2; + std::set num_list3; + std::set num_list4; +/* + for(int n = 0; n < 100000; n++) + { + // come up with some vectors using the full sample space + Vector3int32 v; + v.x = (int)((rand() << 16) ^ rand()); + v.y = (int)((rand() << 16) ^ rand()); + v.z = (int)((rand() << 16) ^ rand()); + + int key = getHash(v); + + if(num_list1.find(key) == num_list1.end()) { num_list1.insert(key); continue; } + if(num_list2.find(key) == num_list2.end()) { num_list2.insert(key); continue; } + if(num_list3.find(key) == num_list3.end()) { num_list3.insert(key); continue; } + if(num_list4.find(key) == num_list4.end()) { num_list4.insert(key); continue; } + } + + int u1 = num_list1.size(); + int u2 = num_list2.size(); + int u3 = num_list3.size(); + int u4 = num_list4.size(); + + RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Random Hash distribution 1: %d 2: %d 3: %d 4: %d", u1, u2, u3, u4); +*/ + + std::vector counts(numBuckets(), 0); + std::vector differentGrids(numBuckets(), 0); + + for (size_t i = 0; i < numBuckets(); ++i) + { + counts[i] = computeNumNodes(nodes[i]); + differentGrids[i] = computeNumGrids(nodes[i]); + } + + sort(counts.begin(), counts.end()); + sort(differentGrids.begin(), differentGrids.end()); + + RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "CURRENT HASH DISTRIBUTION"); + size_t numSlots = 100; + for (size_t i = 0; i < numSlots; ++i) + { + size_t index = ( (i + 1) * numBuckets() / numSlots ) - 1; + RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Slot: %d Count: %d", i, counts[index]); + } + + + RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "TOP HASH DISTRIBUTION"); + for (size_t i = 0; i < numSlots; ++i) + { + size_t index = numBuckets() - 1 - i; + RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Slot: %d Count: %d", i, counts[index]); + } + + RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "TOP GRID COLLISIONS DISTRIBUTION"); + for (size_t i = 0; i < numSlots; ++i) + { + size_t index = numBuckets() - 1 - i; + RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Slot: %d Count: %d", i, differentGrids[index]); + } +#endif +} + +SHP void SpatialHash::registerCoarseMovementCallback( + CoarseMovementCallback* callback) { + coarseMovementCallbacks.push_back(callback); +} + +SHP void SpatialHash::unregisterCoarseMovementCallback( + CoarseMovementCallback* callback) { + typename std::vector::iterator itr = + std::find(coarseMovementCallbacks.begin(), coarseMovementCallbacks.end(), callback); + if (itr != coarseMovementCallbacks.end()) { + coarseMovementCallbacks.erase(itr); + } +} + +} // namespace diff --git a/App/v8world/StepJointsStage.h b/App/v8world/StepJointsStage.h new file mode 100644 index 0000000..61a4606 --- /dev/null +++ b/App/v8world/StepJointsStage.h @@ -0,0 +1,45 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" +#include "V8World/Joint.h" +#include "boost/scoped_ptr.hpp" +#include "boost/intrusive/list.hpp" + +namespace RBX { + class Assembly; + + namespace Profiling + { + class CodeProfiler; + } + + class StepJointsStage : public IWorldStage { + private: + typedef boost::intrusive::list > Joints; + Joints worldStepJoints; + + void addJoint(Joint* j); + void removeJoint(Joint* j); + + public: + /////////////////////////////////////////// + // IStage + StepJointsStage(IStage* upstream, World* world); + + ~StepJointsStage(); + + /*override*/ IStage::StageType getStageType() const {return IStage::STEP_JOINTS_STAGE;} + + /*override*/ void onEdgeAdded(Edge* e); + /*override*/ void onEdgeRemoving(Edge* e); + + void onSimulateAssemblyAdded(Assembly* a); + void onSimulateAssemblyRemoving(Assembly* a); + + void jointsStepWorld(); + + boost::scoped_ptr profilingJointUpdate; + }; +} // namespace diff --git a/App/v8world/SurfaceData.h b/App/v8world/SurfaceData.h new file mode 100644 index 0000000..f8112ad --- /dev/null +++ b/App/v8world/SurfaceData.h @@ -0,0 +1,32 @@ + /* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#include "V8World/Controller.h" + +#pragma once + +namespace RBX { + + class SurfaceData { + public: + LegacyController::InputType inputType; + float paramA; + float paramB; + + SurfaceData() + : inputType(LegacyController::NO_INPUT) + , paramA(-0.5) + , paramB(0.5) + {} + + bool operator== (const SurfaceData& other) const { + return ( inputType == other.inputType + && paramA == other.paramA + && paramB == other.paramB ); + } + + static const SurfaceData& empty() {static SurfaceData s; return s;} + + bool isEmpty() const {return *this == empty();} + }; + +} // namespace \ No newline at end of file diff --git a/App/v8world/TerrainPartition.h b/App/v8world/TerrainPartition.h new file mode 100644 index 0000000..1ca88f8 --- /dev/null +++ b/App/v8world/TerrainPartition.h @@ -0,0 +1,101 @@ +#pragma once +/* Copyright 2003-2011 ROBLOX Corporation, All Rights Reserved */ + +#include +#include "Util/G3DCore.h" +#include "V8World/Primitive.h" +#include "Voxel/Util.h" +#include "Voxel/CellChangeListener.h" +#include "Voxel/ChunkMap.h" +#include "Voxel2/GridListener.h" + +namespace RBX { +namespace Voxel { + class Grid; +} + +namespace Voxel2 { + class Grid; +} + +class TerrainPartitionMega: + public Voxel::CellChangeListener +{ +public: + TerrainPartitionMega(Voxel::Grid* voxelGrid); + ~TerrainPartitionMega(); + + void findCellsTouchingExtents(const Extents& extents, std::vector* found) const; + +private: + struct ChunkData + { + // filled[y][z][x] represents a sub-chunk of 4x2x4 cells, where 1 cell = 1 bit + unsigned int count; + unsigned int filled[Voxel::kY_CHUNK_SIZE / 2][Voxel::kXZ_CHUNK_SIZE / 4][Voxel::kXZ_CHUNK_SIZE / 4]; + + ChunkData(): count(0) + { + memset(filled, 0, sizeof(filled)); + } + }; + + Voxel::ChunkMap chunks; + Voxel::Grid* voxelGrid; + + /*override*/ virtual void terrainCellChanged(const Voxel::CellChangeInfo& info); + + void findCellsInRegion(const SpatialRegion::Id& region, const ChunkData& chunk, const Vector3int16& minOffset, const Vector3int16& maxOffset, std::vector* found) const; +}; + +class TerrainPartitionSmooth +{ +public: + static const int kChunkSizeLog2 = 3; + static const int kChunkSize = 1 << kChunkSizeLog2; + + TerrainPartitionSmooth(Voxel2::Grid* grid); + ~TerrainPartitionSmooth(); + + struct ChunkResult + { + Vector3int32 id; + bool touchesSolid; + bool touchesWater; + }; + + void findChunksTouchingExtents(const Extents& extents, std::vector* found) const; + + void updateChunk(const Vector3int32& id); + +private: + struct ChunkSlice + { + // 8x8 bits for each slice + uint64_t solid; + uint64_t water; + }; + + struct ChunkData + { + ChunkSlice slices[kChunkSize]; + + ChunkData() + { + memset(slices, 0, sizeof(slices)); + } + }; + + typedef boost::unordered_map ChunkMap; + + ChunkMap chunks; + Voxel2::Grid* grid; + + uint64_t masksHor[kChunkSize][kChunkSize]; + uint64_t masksVer[kChunkSize][kChunkSize]; + + void fillChunkIfTouchingExtents(const Vector3int32& chunkId, const ChunkData& chunkData, const Vector3int32& minPos, const Vector3int32& maxPos, std::vector* found) const; +}; + +} // namespace + diff --git a/App/v8world/Tolerance.h b/App/v8world/Tolerance.h new file mode 100644 index 0000000..ea11ab3 --- /dev/null +++ b/App/v8world/Tolerance.h @@ -0,0 +1,59 @@ +#pragma once + +#include "stdafx.h" +#include "Util/G3DCore.h" +#include "Util/Extents.h" +#include "V8Kernel/ContactConnector.h" + + +namespace RBX { + + class Tolerance + { + public: + ////////////////////////////////////////////////////////// + // + static const Extents& maxExtents() { + // cds: this is the no-clip hack patch. 1777.7 is arbitrary. + const float fuzzyMil = 1e6 + 1777.7 + (*((int*)(__DATE__ + 2)) % 1000); + static Extents millionCube(Vector3(-fuzzyMil - (rand()%65536), + -fuzzyMil - (rand()%65536), + -fuzzyMil - (rand()%65536)), + Vector3( fuzzyMil + (rand()%65536), + fuzzyMil + (rand()%65536), + fuzzyMil + (rand()%65536))); + return millionCube; + } + + // Tolerance for joining + static float mainGrid() {return 0.1f;} + static float jointMaxUnaligned() {return 0.05f;} + static float jointOverlapMin() {return 0.35f;} // plate thickness is 0.4 + static float jointOverlapMin2() {return 0.1f;} + + static bool pointsUnaligned(const Vector3& p0, const Vector3& p1) { + float magSqr = (p1-p0).squaredMagnitude(); + return (magSqr > (jointMaxUnaligned() * jointMaxUnaligned())); + } + + // Joint, Spawn: tight parameters, only achieved by a snap + static float jointAngleMax() {return 0.01f;} // radians + static float jointPlanarMax() {return 0.01f;} + + // Rotate: loose parameters + static float rotateAngleMax() {return jointMaxUnaligned() * 0.5f;} // length of the axle is always == 2 + static float rotatePlanarMax() {return jointMaxUnaligned();} + + // Glue: loose parameters + static float glueAngleMax() {return jointMaxUnaligned();} // radians + static float gluePlanarMax() {return jointMaxUnaligned();} + + // Tolerance for dragger and for primitive::fuzzyExtents + // For now this is nasty - it equals the connector overlap tolerance + static float maxOverlapOrGap() {return ContactConnector::overlapGoal();} // 0.01; + + static float maxOverlapAllowedForResize() {return 3.0f * maxOverlapOrGap();} // 0.03; + }; + +} // namespace + diff --git a/App/v8world/TreeStage.h b/App/v8world/TreeStage.h new file mode 100644 index 0000000..8453b9a --- /dev/null +++ b/App/v8world/TreeStage.h @@ -0,0 +1,94 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/IWorldStage.h" +#include "V8World/Enum.h" +#include "Util/Utilities.h" +#include "Util/SpanningTree.h" + +namespace RBX { + + class Primitive; + class Joint; + class Mechanism; + class Clump; + class Edge; + + class JointSort { + public: + static bool heavierJoint(const Joint* j0, const Joint* j1); + }; + + class TreeStage : public IWorldStage + , public SpanningTree + { + private: + typedef SpanningTree Super; + int maxTreeDepth; + + /////////////////////////////////////////////////////// + /////////////////////////////////////////////////////// + // + // Data storage + std::set dirtyMechanisms; + std::set downstreamMechanisms; + + /////////////////////////////////////////////////////// + // Traverse Utilities + // + void removeSpanningTreeJoint(Joint* j); + void swapTree(Joint* deactivate, Joint* activate, Primitive* newParent); + + /////////////////////////////////////////////// + // Spanning Tree + // + /*override*/ void onSpanningEdgeAdding(SpanningEdge* edge, SpanningNode* child); + /*override*/ void onSpanningEdgeAdded(SpanningEdge* edge); + /*override*/ void onSpanningEdgeRemoving(SpanningEdge* edge); + /*override*/ void onSpanningEdgeRemoved(SpanningEdge* edge, SpanningNode* child); + /*override*/ bool validateTree(SpanningNode* root); + + void removeFromPipeline(Mechanism* m); + void dirtyMechanism(Mechanism* m); + void cleanMechanism(Mechanism* m); // true if moved downstream + + void destroyClump(Primitive* p); + void destroyAssembly(Primitive* p); + void destroyMechanism(Primitive* p); + + public: + /////////////////////////////////////////// + // IStage + TreeStage(IStage* upstream, World* world); + + ~TreeStage(); + + /*override*/ IStage::StageType getStageType() const {return IStage::TREE_STAGE;} + + /*override*/ void onEdgeAdded(Edge* e); + /*override*/ void onEdgeRemoving(Edge* e); + + /*override*/ int getMetric(IWorldStage::MetricType metricType); + + ///////////////////////////////////////////// + // From the Joint Stage + // + void onPrimitiveAdded(Primitive* p); + void onPrimitiveRemoving(Primitive* p); + + ///////////////////////////////////////////// + // From the World + // + + void assemble(); // update everything; + + bool isAssembled() const {return dirtyMechanisms.empty();} + + // from internal and world + void sendClumpChangedMessage(Primitive* childPrim); + }; + +} // namespace + + diff --git a/App/v8world/TriangleMesh.h b/App/v8world/TriangleMesh.h new file mode 100644 index 0000000..c048536 --- /dev/null +++ b/App/v8world/TriangleMesh.h @@ -0,0 +1,163 @@ +#pragma once + +#include "V8World/Geometry.h" +#include "V8World/Mesh.h" +#include "V8World/Block.h" +#include "V8World/BulletGeometryPoolObjects.h" +#include "Extras/ConvexDecomposition/ConvexDecomposition.h" + +#include "v8world/KDTree.h" + +#define PHYSICS_SERIAL_VERSION 3 + +namespace RBX +{ + class CSGConvex; + class ConvexPoly; + class Block; + + class KDTreeMeshWrapper: public Allocator + { + public: + KDTreeMeshWrapper(const std::string& str); + ~KDTreeMeshWrapper(); + + const KDTree& getTree() const { return tree; } + + private: + std::vector vertices; + std::vector indices; + KDTree tree; + }; + + class TriangleMesh : public Geometry + { + public: + typedef GeometryPool BulletDecompPool; + typedef GeometryPool KDTreeMeshPool; + + private: + //decompData + int version; + BulletDecompPool::Token compound; + + KDTreeMeshPool::Token kdTreeMesh; + Vector3 kdTreeScale; + + // Needed for basic Dragger functions + Block* boundingBoxMesh; + + typedef Geometry Super; + float centerToCornerDistance; + + Matrix3 getMomentHollow(float mass) const; + + /*override*/ void setSize(const G3D::Vector3& _size); + + public: + TriangleMesh() : version(PHYSICS_SERIAL_VERSION), centerToCornerDistance(0.0), boundingBoxMesh(NULL) + { + boundingBoxMesh = new Block(); + bulletCollisionObject.reset(new btCollisionObject()); + } + ~TriangleMesh(); + + // Getters + const BulletDecompWrapper* getCompound() const { return compound ? &*compound : NULL; } + + int getVersion() { return version; } + + static bool validateDataVersions(const std::string& data, int& version); + static bool validateIsBlockData(const std::string& data); + + // Physics Data Setters + void setStaticMeshData(const std::string &key, const std::string& data, const btVector3& scale = btVector3(1.0f, 1.0f, 1.0f)); + bool setCompoundMeshData(const std::string &key, const std::string& data, const btVector3& scale = btVector3(1.0f, 1.0f, 1.0f)); + + // Updates Physics Data + void updateObjectScale(const std::string& decompKey, const std::string &decompStr, const G3D::Vector3& scale, const G3D::Vector3& meshScale = Vector3(-1, -1, -1)); + + // Deserializations + static std::string generateDecompositionData(int numTriangles, const unsigned int* triangleIndexBase, int numVertices, btScalar* vertexBase); + static std::string generateConvexHullData(int numTriangles, const unsigned int* triangleIndexBase, int numVertices, const btVector3* vertexBase); + static BulletDecompWrapper::ShapeType* retrieveDecomposition(const std::string& str); + static std::string generateStaticMeshData(const std::vector& indices, std::vector& vertices); + static void readConvexHullData(std::vector &vertices, unsigned int &numVertices, std::vector &indices, unsigned int &numIndices, btTransform &trans, std::stringstream &stream); + static void readPrefixData(btVector3 &scale, int ¤tVersion, std::stringstream &stream); + + // HOUSEKEEPING + static std::vector getDecompConvexes(const std::string& data, int& currentVersion, btVector3 &scale, bool dataHasScale = false); + static void serializeConvexHullData(const btTransform& transform, const unsigned int numVertices, const float* verticesBase, + const unsigned int numIndices, const unsigned int* indicesBase, std::stringstream &outstream); + + // Creates decomposition data + std::string generateDecompositionGeometry(const std::vector &vertices, const std::vector &indices); + + // UTIL + static const std::string getPlaceholderData(); + static const std::string getBlockData(); + + // Primitive Overrides + /*override*/ virtual bool hitTest(const RbxRay& rayInMe, Vector3& localHitPoint, Vector3& surfaceNormal); + + /*override*/ virtual GeometryType getGeometryType() const {return GEOMETRY_TRI_MESH;} + /*override*/ virtual CollideType getCollideType() const {return COLLIDE_BULLET;} + + // Real Radius + /*override*/ virtual float getRadius() const {return centerToCornerDistance;} + + // Real Corner + /*override*/ virtual Vector3 getCenterToCorner(const Matrix3& rotation) const + { + if (boundingBoxMesh) + return boundingBoxMesh->getCenterToCorner(rotation); + else + return Vector3(centerToCornerDistance, centerToCornerDistance, centerToCornerDistance); + } + + // Moment + /*override*/ virtual Matrix3 getMoment(float mass) const { + return getMomentHollow(mass); + } + + // Dragger Functions + size_t closestSurfaceToPoint( const Vector3& pointInBody ) const; + Plane getPlaneFromSurface( const size_t surfaceId ) const; + virtual CoordinateFrame getSurfaceCoordInBody( const size_t surfaceId ) const; + Vector3 getSurfaceNormalInBody( const size_t surfaceId ) const; + size_t getMostAlignedSurface( const Vector3& vecInWorld, const G3D::Matrix3& objectR ) const; + int getNumSurfaces( void ) const { return boundingBoxMesh->getMesh()->numFaces(); } + Vector3 getSurfaceVertInBody( const size_t surfaceId, const int vertId ) const; + int getNumVertsInSurface( const size_t surfaceId ) const; + bool vertOverlapsFace( const Vector3& pointInBody, const size_t surfaceId ) const; + + /*override*/virtual bool findTouchingSurfacesConvex( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId ) const; + /*override*/virtual bool FacesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const; + /*override*/virtual bool FaceVerticesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const; + /*override*/virtual bool FaceEdgesOverlapped( const CoordinateFrame& myCf, size_t& myFaceId, const Geometry& otherGeom, const CoordinateFrame& otherCf, size_t& otherFaceId, float tol ) const; + + /*override*/ bool setUpBulletCollisionData(void); + + }; + + class CSGConvex + { + public: + std::vector vertices; + std::vector indices; + btTransform transform; + }; + + class BulletConvexDecomposition : public ConvexDecomposition::ConvexDecompInterface + { + private: + std::stringstream streamChildren; //binary string stream for vertex data + + public: + BulletConvexDecomposition() {} + + virtual void ConvexDecompResult(ConvexDecomposition::ConvexResult &result); + void addStreamChildren(std::stringstream &streamString); + + }; +} // namespace diff --git a/App/v8world/WedgeMesh.h b/App/v8world/WedgeMesh.h new file mode 100644 index 0000000..6b88f90 --- /dev/null +++ b/App/v8world/WedgeMesh.h @@ -0,0 +1,29 @@ +#pragma once + +/* + Utility class - holds Wedge Meshes of same size for use by Geometry Pool. +*/ + +#include "Util/Memory.h" +#include "V8World/Mesh.h" + + +namespace RBX { + + namespace POLY { + + class WedgeMesh : public Allocator + { + private: + Mesh mesh; + + public: + WedgeMesh(const Vector3& size) + { + mesh.makeWedge(size); + } + const Mesh* getMesh() const {return &mesh;} + }; + + } // namespace POLY +} // namespace RBX diff --git a/App/v8world/WedgePoly.h b/App/v8world/WedgePoly.h new file mode 100644 index 0000000..b6fa6f0 --- /dev/null +++ b/App/v8world/WedgePoly.h @@ -0,0 +1,42 @@ +#pragma once + +#include "V8World/Poly.h" +#include "V8World/GeometryPool.h" +#include "V8World/WedgeMesh.h" +#include "V8World/BlockMesh.h" +#include "V8World/BulletGeometryPoolObjects.h" + +namespace RBX { + + class WedgePoly : public Poly { + public: + typedef GeometryPool WedgeMeshPool; + typedef GeometryPool BulletWedgeShapePool; + + /*override*/ Matrix3 getMoment(float mass) const; + /*override*/ Vector3 getCofmOffset() const; + /*override*/ CoordinateFrame getSurfaceCoordInBody( const size_t surfaceId ) const; + /*override*/ size_t getFaceFromLegacyNormalId( const NormalId nId ) const; + /*override*/ bool isGeometryOrthogonal( void ) const { return false; } + /*override*/ bool setUpBulletCollisionData(void); + /*override*/ void setSize(const G3D::Vector3& _size); + + private: + typedef Poly Super; + + WedgeMeshPool::Token wedgeMesh; + BulletWedgeShapePool::Token bulletWedgeShape; + + /*override*/ virtual Vector3 getCenterToCorner(const Matrix3& rotation) const; + + void updateBulletCollisionData(); + + protected: + // Geometry Overrides + /*override*/ virtual GeometryType getGeometryType() const {return GEOMETRY_WEDGE;} + + // Poly Overrides + /*override*/ void buildMesh(); + }; + +} // namespace diff --git a/App/v8world/WeldJoint.h b/App/v8world/WeldJoint.h new file mode 100644 index 0000000..176a029 --- /dev/null +++ b/App/v8world/WeldJoint.h @@ -0,0 +1,67 @@ +#pragma once + +#include "V8World/RigidJoint.h" + +namespace RBX { + + class WeldJoint : public RigidJoint + { + private: + /////////////////////////////////////////////////// + // Joint + /*override*/ virtual JointType getJointType() const {return WELD_JOINT;} + + /////////////////////////////////////////////////// + // WeldJoint + static bool compatibleSurfaces( + Primitive* p0, + Primitive* p1, + NormalId nId0, + NormalId nId1); + + public: + WeldJoint() {} + + WeldJoint(Primitive* prim0, Primitive* prim1, const CoordinateFrame& c0, const CoordinateFrame &c1) + : RigidJoint(prim0, prim1, c0, c1) + {} + + virtual ~WeldJoint() {} + + static WeldJoint* canBuildJoint( + Primitive* p0, + Primitive* p1, + NormalId nId0, + NormalId nId1); + }; + + class ManualWeldJoint : public WeldJoint + { + private: + size_t surface0; // surface from primitive 0 + size_t surface1; // surface from primitive 1 + + /*override*/ virtual JointType getJointType() const {return MANUAL_WELD_JOINT;} + + public: + ManualWeldJoint() {surface0 = (size_t)-1; surface1 = (size_t)-1;} + ManualWeldJoint(size_t s0, size_t s1, Primitive* prim0, Primitive* prim1, const CoordinateFrame& c0, const CoordinateFrame &c1) + : WeldJoint(prim0, prim1, c0, c1) + {surface0 = s0; surface1 = s1;} + + ~ManualWeldJoint() {} + + size_t getSurface0(void) const {return surface0;} + size_t getSurface1(void) const {return surface1;} + void setSurface0(size_t surfId) {surface0 = surfId;} + void setSurface1(size_t surfId) {surface1 = surfId;} + + Vector3int16 getCell() const; + void setCell(const Vector3int16& pos); + + static bool isTouchingTerrain(Primitive* terrain, Primitive* prim); + }; + + +} // namespace + diff --git a/App/v8world/World.h b/App/v8world/World.h new file mode 100644 index 0000000..1697d9a --- /dev/null +++ b/App/v8world/World.h @@ -0,0 +1,353 @@ +/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ + +#pragma once + +#include "V8World/Primitive.h" +#include "V8World/ContactManager.h" + +#include "Util/IndexArray.h" +#include "rbx/rbxTime.h" +#include "rbx/signal.h" +#include "Util/SpatialRegion.h" +#include "Util/HeapValue.h" +#include "util/PhysicalProperties.h" + +class btCollisionDispatcher; +class btCollisionWorld; +class btDefaultCollisionConfiguration; +namespace RBX { + + class JointInstance; + class Joint; + class Primitive; + class Clump; + class Assembly; + class Region2; + class PartInstance; + + namespace Profiling + { + class CodeProfiler; + } + + struct RootPrimitiveOwnershipData + { + RBX::SystemAddress ownerAddress; + bool ownershipManual; + Primitive* prim; + }; + + class Edge; + class Contact; + class MotorJoint; + + // In Assembly.cpp + void notifyAssemblyPrimitiveMoved(Primitive* p, bool resetContacts); + + class EThrottle { + public: + typedef enum { ThrottleDefaultAuto, ThrottleDisabled, ThrottleAlways, Skip2, Skip4, Skip8, Skip16} EThrottleType; + + private: + int requestedSkip; + int usedSkip; + static int const throttleSetting[]; + int throttleIndex; + + public: + static EThrottleType globalDebugEThrottle; + + EThrottle(); + + bool computeThrottle(int step); + + bool increaseLoad(bool increase); + + float getEnvironmentSpeed() const; + + int getThrottleIndex() const { return throttleIndex; } + + void setThrottleIndex(int index) { throttleIndex = index; } + + }; + + + class World + { + friend class ContactManager; + public: + rbx::signal&)> postInsertJointSignal; + rbx::signal&, std::vector&)> postRemoveJointSignal; + rbx::signal autoJoinSignal; + rbx::signal autoDestroySignal; + rbx::signal)> primitiveCollideSignal; + + struct TouchInfo + { + Primitive* p1; + Primitive* p2; + shared_ptr pi1; + shared_ptr pi2; + typedef enum { Touch, Untouch } Type; + Type type; + }; + + struct OnPrimitiveMovingVisitor + { + ContactManager* ptr; + OnPrimitiveMovingVisitor(ContactManager* p) : ptr(p) { } + void operator()(Primitive* p) { + notifyAssemblyPrimitiveMoved(p, true); + ptr->onPrimitiveExtentsChanged(p); + } + }; + + private: + + int frmThrottle; + EThrottle eThrottle; + + G3D::Array touchReporting; + + bool inStepCode; // debugging + Joint* inJointNotification; + + int worldSteps; + int worldStepId; // two years at 1/30 second dt + float worldStepAccumulated; // Time unaccounted for in the last step + + HeapValue fallenPartDestroyHeight; + + // defining objects + IndexArray primitives; + Primitive* groundPrimitive; // for now, only used by kernel joints + + btCollisionDispatcher* bulletDispatcher; + btDefaultCollisionConfiguration* bulletCollisionConfiguration; + + // Generates unique ids for objects registered with this World + boost::uint64_t UIDGenerator; + bool usingPGSSolver; + bool physicsAnalyzerEnabled; + + // Material Properties + PhysicalPropertiesMode physicalMaterialsMode; + + // Networked Interpolation Sync + Time lastFrameTimeStamp; // Timestamp of current frame beginning + Time lastSendTimeStamp; // Timestamp of the frame where we last sent physics + int lastNumWorldSteps; + double worldStepOffset; + + public: + float getUpdateExpectedStepDelta(); + + btCollisionDispatcher* getBulletCollisionDispatcher(void) { return bulletDispatcher; } + + private: + + // redundant data + G3D::Array movingPrimitives; + std::set breakableJoints; + int numJoints; + int numContacts; + int numLinkCalls; + + // motion analytic + double errorCount; + double passCount; + double frameinfosSize; + double frameinfosTarget; + double targetDelayTenths; + double infosSizeTenths; + double maxDelta; + double frameinfosCount; + + + // redundant data - performance - prevent allocation + G3D::Array tempPrimitives; + G3D::Array > tempJoints; + + boost::unordered_map< boost::uint64_t, Primitive* > primitiveIndexation; + + boost::scoped_ptr profilingBreak; + boost::scoped_ptr profilingAssembly; + boost::scoped_ptr profilingFilter; + boost::scoped_ptr profilingWorldStep; + boost::scoped_ptr profilingUiStep; + + void createAutoJoints(Primitive* p, std::set* ignoreGroup, std::set* joinGroup); // if joinGroup, only connect with them + void destroyAutoJoints(Primitive* p, std::set* ignoreGroup, bool includeExplicit = true, bool includeAuto = true); // if group, then keep joints between group members + + void destroyJoint(Joint* j); + + void removeFromBreakable(Joint* j); + + // these functions are in the main world->step() loop + void doBreakJoints(); // goes through the breakableJoints, breaks if necessary; + + void uiStep(bool longStep, double distributedGameTime); + void doWorldStep(bool throttling, int uiStepId, int numThreads, boost::uint64_t debugTime); + + void notifyMovingAssemblies(); + + ContactManager* contactManager; + class SendPhysics* sendPhysics; + class CleanStage* cleanStage; + + class GroundStage* getGroundStage(); + class SleepStage* getSleepStage(); + class TreeStage* getTreeStage(); + const class SpatialFilter* getSpatialFilter() const; + class AssemblyStage* getAssemblyStage(); + const AssemblyStage* getAssemblyStage() const; + class MovingAssemblyStage* getMovingAssemblyStage(); + class StepJointsStage* getStepJointsStage(); + const StepJointsStage* getStepJointsStage() const; + const class SleepStage* getSleepStage() const; + class SimulateStage* getSimulateStage(); + + public: + World(); + ~World(); + + void assertNotInStep() { RBXASSERT(!inStepCode); } + void assertInStep() { RBXASSERT(inStepCode); } + + class SendPhysics* getSendPhysics(); + class SimSendFilter& getSimSendFilter(); + SpatialFilter* getSpatialFilter(); + ContactManager* getContactManager() {return contactManager;} + const ContactManager* getContactManager() const {return contactManager;} + class HumanoidStage* getHumanoidStage(); + class Kernel* getKernel(); + const Kernel* getKernel() const; + + const G3D::Array& getTouchInfoFromLastStep() {return touchReporting;} + void clearTouchInfoFromLastStep() {touchReporting.fastClear();} + + void computeFallen(G3D::Array& fallen) const; + + const G3D::Array& getPrimitives() const {return primitives.underlyingArray();} + + // engine interface + int updateStepsRequiredForCyclicExecutive(float desiredInterval); + float step(bool longStep, double distributedGameTime, float desiredInterval, int numThreads); // 10-100 frames per second + void assemble(); // on heartbeat, before collision detection + bool isAssembled(); + void reset() {RBXASSERT(!inStepCode); worldStepId = 0;} + int getWorldStepId() {return worldStepId;} + float getWorldStepsAccumulated() { return worldStepAccumulated; } + int getUiStepId(); + int getLongUiStepId(); + void sendClumpChangedMessage(Primitive* childPrim); + + EThrottle& getEThrottle() {return eThrottle;} + int getFRMThrottle() { return frmThrottle;} + void setFRMThrottle(int value); + + Primitive* getGroundPrimitive() {return groundPrimitive;} + + // PRIMITIVE - Runtime geometry manipulation + void insertPrimitive(Primitive* p); + void removePrimitive(Primitive* p, bool isStreamingRemove); + void ticklePrimitive(Primitive* p, bool recursive); // simulates a touch, wakes up + Primitive* getPrimitiveFromBodyUID( boost::uint64_t uid ) const; + + // Auto Joining / Unjoining functions + void joinAll(); + + void createAutoJoints(Primitive* p); + void createAutoJointsToWorld(const G3D::Array& primitives); // ignores joints between them + void createAutoJointsToPrimitives(const G3D::Array& primitives); // only join each other in this group + + void destroyAutoJoints(Primitive* p, bool includeExplicit = true); + void destroyAutoJointsToWorld(const G3D::Array& primitives); // ignores joints between them + + void destroyTerrainWeldJointsWithEmptyCells(Primitive* megaClusterPrim, const SpatialRegion::Id& region, Primitive* touchingPrim); + void destroyTerrainWeldJointsNoTouch(Primitive* megaClusterPrim, Primitive* touchingPrim); + + // Joint based insert, remove + void insertJoint(Joint* j); + void removeJoint(Joint* j); + void jointCoordsChanged(Joint* j); + void notifyMoved(Primitive* p); + + // Network Ownership API Data Gatherer + void gatherMechDataPreJoin(Joint *j, Primitive*& unGroundedPrim, std::vector& combiningRoots); + void gatherMechDataPreSplit(Joint* j, std::vector& prim0ChildRoots, std::vector& prim1Roots); + + // CONTACT MANAGER - Can only be called by the contact manager; + void insertContact(Contact* c); + void destroyContact(Contact* c); + + // inquiry functions + int getMetric(IWorldStage::MetricType metricType) const; + int getNumBodies() const; + int getNumPoints() const; + int getNumConstraints() const; + int getNumHashNodes() const; + int getMaxBucketSize() const; + int getNumLinkCalls() const {return numLinkCalls;} + int getNumContacts() const {return numContacts;} + int getNumJoints() const {return numJoints;} + int getNumPrimitives() const {return getPrimitives().size();} + float getEnvironmentSpeed() const {return eThrottle.getEnvironmentSpeed();} + float getEnvironmentSpeedPercent() const {return getEnvironmentSpeed() * 100.0f;} + + void setFallenPartDestroyHeight(float value) {fallenPartDestroyHeight = value;} + float getFallenPartDestroyHeight() const {return fallenPartDestroyHeight;} + + RBX::Profiling::CodeProfiler& getProfileWorldStep() { return *profilingWorldStep; } + const RBX::Profiling::CodeProfiler& getProfileWorldStep() const { return *profilingWorldStep; } + + void loadProfilers(std::vector& worldProfilers) const; + + // PRIMITIVE - notification on edits - only called by Primitive or Clump + Assembly* onPrimitiveEngineChanging(Primitive* p); + void onPrimitiveEngineChanged(Assembly* changing); + + void onPrimitiveFixedChanging(Primitive* p); + void onPrimitiveFixedChanged(Primitive* p); + + void onPrimitivePreventCollideChanged(Primitive* p); + void onPrimitiveExtentsChanged(Primitive* p); + void onPrimitiveContactParametersChanged(Primitive* p); + void onPrimitiveGeometryChanged(Primitive* p); + void reportTouchInfo(const TouchInfo& info); + void reportTouchInfo(Primitive* p0, Primitive* p1, World::TouchInfo::Type T); + + void onPrimitiveCollided(Primitive* p0, Primitive* p1); + + void onAssemblyPhysicsChanged(Assembly* a, bool physics) const; + void onAssemblyInSimluationStage(Assembly* a); + + // JOINT - notification on edits - only called by Joint + void onJointPrimitiveNulling(Joint* j, Primitive* p); + void onJointPrimitiveSet(Joint* j, Primitive* p); + + void addAnimatedJointToMovingAssemblyStage(Joint* j); + void removeAnimatedJointFromMovingAssemblyStage(Joint* j); + + bool getUsingPGSSolver(); + void setUsingPGSSolver(bool usePGS); + void setUserId( int id ); + + void setPhysicsAnalyzerEnabled(bool value) { physicsAnalyzerEnabled = value; } + + bool getUsingNewPhysicalProperties() const; + + PhysicalPropertiesMode getPhysicalPropertiesMode() const { return physicalMaterialsMode; } + void setPhysicalPropertiesMode(PhysicalPropertiesMode mode); + + // motion analytic + void plusErrorCount(double ec) { errorCount += ec;} + void plusPassCount(double pc) { passCount += pc; } + double getPassCount() {return passCount; } + void sendAnalytics(void); + void addFrameinfosStat(double fis, double fit, double tdt, double ist, double md, double fic); + }; + +} // namespace + + diff --git a/App/v8xml/Reference.h b/App/v8xml/Reference.h new file mode 100644 index 0000000..d2f134c --- /dev/null +++ b/App/v8xml/Reference.h @@ -0,0 +1,48 @@ + +#ifndef _568E368F53F1431aB7D4923F6D45021A +#define _568E368F53F1431aB7D4923F6D45021A + +#include "rbx/Debug.h" + +#include "Util/Object.h" +#include "Util/Handle.h" + +#include +#include +#include +#include + +class XmlNameValuePair; + +namespace RBX { + + namespace Reflection + { + class DescribedBase; + } + + // Used for streaming + class RBXInterface IIDREF + { + private: + friend class IReferenceBinder; + virtual void assignIDREF(Reflection::DescribedBase* propertyOwner, const InstanceHandle& handle) const = 0; + }; + + // Used for streaming + class RBXBaseClass IReferenceBinder + { + public: + virtual void announceID(const XmlNameValuePair* valueID, Reflection::DescribedBase* source) = 0; + virtual void announceIDREF(const XmlNameValuePair* valueIDREF, Reflection::DescribedBase* propertyOwner, const IIDREF* idref) = 0; + virtual bool resolveRefs() = 0; + virtual ~IReferenceBinder(){} + protected: + void assign(const IIDREF* idref, Reflection::DescribedBase* propertyOwner, const InstanceHandle& handle) { + idref->assignIDREF(propertyOwner, handle); + } + }; + +} + +#endif diff --git a/App/v8xml/Serializer.h b/App/v8xml/Serializer.h new file mode 100644 index 0000000..b0bfe14 --- /dev/null +++ b/App/v8xml/Serializer.h @@ -0,0 +1,72 @@ +#ifndef V8XML_SERIALIZER_H +#define V8XML_SERIALIZER_H + +#pragma once + +#include "SerializerV2.h" + +#include "util/SoundService.h" + +#include "v8datamodel/Workspace.h" +#include "v8datamodel/Lighting.h" +#include "v8datamodel/ServerStorage.h" +#include "v8datamodel/ReplicatedStorage.h" +#include "v8datamodel/ReplicatedFirst.h" +#include "v8datamodel/PlayerGui.h" +#include "v8datamodel/Hopper.h" +#include "v8datamodel/StarterPlayerService.h" +#include "v8datamodel/ServerScriptService.h" +#include "v8datamodel/CSGDictionaryService.h" + +class Serializer : public SerializerV2 +{ +public: + static bool canWriteChild(const shared_ptr instance, RBX::Instance::SaveFilter saveFilter) + { + if(!instance->getIsArchivable()) + return false; + + switch(saveFilter) + { + case RBX::Instance::SAVE_ALL: + return true; + + case RBX::Instance::SAVE_WORLD: + if ( RBX::Instance::fastDynamicCast(instance.get()) ) + return true; + if ( RBX::Instance::fastDynamicCast(instance.get()) ) + return true; + if ( RBX::Instance::fastDynamicCast(instance.get()) ) + return true; + if ( RBX::Instance::fastDynamicCast(instance.get()) ) + return true; + if ( RBX::Instance::fastDynamicCast(instance.get()) ) + return true; + if ( RBX::Instance::fastDynamicCast(instance.get()) ) + return true; + + return false; + + case RBX::Instance::SAVE_GAME: + if ( RBX::Instance::fastDynamicCast(instance.get()) ) + return true; + if ( RBX::Instance::fastDynamicCast(instance.get()) ) + return true; + if ( RBX::Instance::fastDynamicCast(instance.get()) ) + return true; + if ( RBX::Instance::fastDynamicCast(instance.get()) ) + return true; + if ( RBX::Instance::fastDynamicCast(instance.get()) ) + return true; + + return false; + + default: + return true; + } + } +}; + + + +#endif \ No newline at end of file diff --git a/App/v8xml/SerializerBinary.h b/App/v8xml/SerializerBinary.h new file mode 100644 index 0000000..bce9e69 --- /dev/null +++ b/App/v8xml/SerializerBinary.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include + +namespace RBX +{ + namespace SerializerBinary + { + enum SerializeFlags + { + sfHighCompression = 1 << 0, + sfNoCompression = 1 << 1, + sfInexactCFrame = 1 << 2 + }; + + static const char kMagicHeader[] = " +#include +#include +#include "V8Xml/XmlElement.h" +#include "V8tree/instance.h" +#include "V8Xml/Reference.h" +#include "Util/Object.h" +//typedef std::vector ModelList; + + + +namespace RBX { + class DataModel; +} + + +#if defined(G3D_WIN32) +#pragma warning(push) +#pragma warning(disable:4290) +#endif + +// TODO: Refactor: Call this RBX::DOM or something +class SerializerV2 { +protected: + int schemaVersionLoading; +public: + static const int CURRENT_SCHEMA_VERSION = 4; + + // writing: + static XmlElement* newRootElement(); + static XmlElement* newRootElement(const std::string& type); + + // reading: + void loadInstancesFromText(const XmlElement* root, RBX::Instances& result); + + // Until DataModel becomes an Instance and it can handle "globals" like Workspace, we need to treat + // it specially during reads: + void load(std::istream& stream, RBX::DataModel* dataModel); + void loadInstances(std::istream& stream, RBX::Instances& result); + +private: + void loadXML(std::istream& stream, RBX::DataModel* dataModel); + void loadInstancesXML(const XmlElement* root, RBX::Instances& result, RBX::IReferenceBinder& binder, RBX::CreatorRole creatorRole); + shared_ptr loadInstanceXML(const XmlElement* itemElement, RBX::IReferenceBinder& binder, RBX::CreatorRole creatorRole); +}; + +#if defined(G3D_WIN32) +#pragma warning(pop) +#endif + + +namespace RBX +{ + // MergeBinder is used to merge an XML stream into an existing world (for undo/redo operations) + class MergeBinder : public IReferenceBinder + { + struct IDREFItem { + const IIDREF* idref; + Reflection::DescribedBase* propertyOwner; + RBX::InstanceHandle value; + }; + + // TODO: vector or list??? + std::vector deferredIDREFItems; + + public: + virtual void announceID(const XmlNameValuePair* valueID, Reflection::DescribedBase* target) { + processID(valueID, target); + } + virtual void announceIDREF(const XmlNameValuePair* valueIDREF, Reflection::DescribedBase* propertyOwner, const IIDREF* idref) { + bool processedIDREF = processIDREF(valueIDREF, propertyOwner, idref); + RBXASSERT(processedIDREF); + } + + virtual bool resolveRefs() { + for (std::vector::iterator iter = deferredIDREFItems.begin(); iter!=deferredIDREFItems.end(); ++iter) + { + assign(iter->idref, iter->propertyOwner, iter->value); + + + } + deferredIDREFItems.clear(); + + return true; + } + + protected: + virtual bool processID(const XmlNameValuePair* valueID, Reflection::DescribedBase* source) { + RBX::InstanceHandle h; + if (valueID->getValue(h)) { + h.linkTo(shared_from(source)); + return true; + } else if (valueID->isValueEqual(&value_IDREF_nil)) + return true; // "nil" means "skip this value", like xsi:nil does for other types + else + return false; + } + virtual bool processIDREF(const XmlNameValuePair* valueIDREF, Reflection::DescribedBase* propertyOwner, const IIDREF* idref) { + RBX::InstanceHandle value; + if (valueIDREF->getValue(value)) { + if (!value.empty()) + assign(idref, propertyOwner, value); + else { + IDREFItem item = {idref, propertyOwner, value}; + deferredIDREFItems.push_back(item); + } + return true; + } else if (valueIDREF->isValueEqual(&value_IDREF_nil)) + return true; // "nil" means "skip this value", like xsi:nil does for other types + else + return false; + } + }; +} + + + + + diff --git a/App/v8xml/WebParser.h b/App/v8xml/WebParser.h new file mode 100644 index 0000000..cc4cc7d --- /dev/null +++ b/App/v8xml/WebParser.h @@ -0,0 +1,39 @@ +#pragma once + +#include "Reflection/Type.h" + +class XmlElement; + +namespace RBX +{ + class WebParser + { + private: + static boost::mutex JSONmutex; + public: + + typedef enum + { + FailOnNonJSON, + SkipNonJSON + } NonJSONBehavior; + + static bool parseWebGenericResponse(std::istream& stream, RBX::Reflection::Variant& result); + static bool parseWebGenericResponse(const XmlElement* root, RBX::Reflection::Variant& result); + static bool parseWebListResponse(std::istream& stream, RBX::Reflection::ValueArray& result); + static bool legacyParseWebJSONResponse(std::stringstream& rawWebResponse, shared_ptr& valueTable); + static bool ptreeParseWebJSONResponse(std::stringstream& rawWebResponse, shared_ptr& valueTable); + + static bool parseJSONTable(const std::string& rawWebResponse, shared_ptr& valueTable); + static bool parseJSONArray(const std::string& rawWebResponse, shared_ptr& valueArray); + static bool parseJSONObject(const std::string& rawWebResponse, Reflection::Variant& result); + + static bool writeJSON(const Reflection::Variant& value, std::string& result, NonJSONBehavior skip = SkipNonJSON); + + protected: + static bool loadTable(const XmlElement* tableElement, RBX::Reflection::ValueMap& result); + static bool loadList(const XmlElement* listElement, RBX::Reflection::ValueArray& result); + static bool loadEntry(const XmlElement* entryElement, std::string& key, RBX::Reflection::Variant& value); + static bool loadValue(const XmlElement* valueElement, RBX::Reflection::Variant& value); + }; +} diff --git a/App/v8xml/WebSerializer.h b/App/v8xml/WebSerializer.h new file mode 100644 index 0000000..0995d3e --- /dev/null +++ b/App/v8xml/WebSerializer.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Reflection/Type.h" + +class XmlElement; + +namespace RBX +{ + class WebSerializer { + public: + static XmlElement* writeTable(const RBX::Reflection::ValueMap& result); + static XmlElement* writeList(const RBX::Reflection::ValueArray& result); + static XmlElement* writeEntry(const std::string& key, const RBX::Reflection::Variant& value); + static XmlElement* writeValue(const RBX::Reflection::Variant& value); + }; +} diff --git a/App/v8xml/XmlElement.h b/App/v8xml/XmlElement.h new file mode 100644 index 0000000..8d94d35 --- /dev/null +++ b/App/v8xml/XmlElement.h @@ -0,0 +1,417 @@ +#ifndef V8XML_XMLELEMENT_H +#define V8XML_XMLELEMENT_H + +#include "G3D/Vector3.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Color3.h" +#include "rbx/Debug.h" +#include "Util/Name.h" +#include "Util/Memory.h" +#include "util/Utilities.h" +#include "util/ContentId.h" + +#include +#include +#include +#include + +#include"Util/Utilities.h" +#include"Util/Handle.h" + +//typedef std::string XmlTag; +typedef RBX::Name XmlTag; + + +// w3 XSD schema specification states that IDREF can't have empty values and can't have the xsi:nil attribute. +// We use xsi:nil to signify "don't change your value when reading me" +// As a result, we define 2 special IDREF strings "null" and "nil" +// +// "nil" means "don't change your current value +// +// for an IDREF "null" means "set your value to NULL" +extern const RBX::Name& value_IDREF_null; +extern const RBX::Name& value_IDREF_nil; + +// TODO: Put these in a file that knows about the Roblox schema +extern const XmlTag& name_root; +extern const XmlTag& tag_roblox; +extern const XmlTag& name_xsinil; +extern const XmlTag& name_xsitype; +extern const XmlTag& tag_xmlnsxsi; +extern const XmlTag& tag_xsinoNamespaceSchemaLocation; +extern const XmlTag& tag_version; +extern const XmlTag& tag_assettype; +extern const XmlTag& name_referent; +extern const XmlTag& tag_External; +extern const XmlTag& name_Ref; +extern const XmlTag& name_token; +extern const XmlTag& name_name; +extern const XmlTag& tag_bool; +extern const XmlTag& tag_Refs; +extern const XmlTag& tag_X; +extern const XmlTag& tag_Y; +extern const XmlTag& tag_Z; + +extern const XmlTag& tag_R00; +extern const XmlTag& tag_R01; +extern const XmlTag& tag_R02; +extern const XmlTag& tag_R10; +extern const XmlTag& tag_R11; +extern const XmlTag& tag_R12; +extern const XmlTag& tag_R20; +extern const XmlTag& tag_R21; +extern const XmlTag& tag_R22; + +extern const XmlTag& tag_R; +extern const XmlTag& tag_G; +extern const XmlTag& tag_B; +extern const XmlTag& tag_class; +extern const XmlTag& tag_Item; +extern const XmlTag& tag_Properties; +extern const XmlTag& tag_Feature; + +extern const XmlTag& tag_hash; +extern const XmlTag& tag_null; +extern const XmlTag& tag_mimeType; + +extern const XmlTag& tag_S; +extern const XmlTag& tag_O; + +extern const XmlTag& tag_XS; +extern const XmlTag& tag_XO; +extern const XmlTag& tag_YS; +extern const XmlTag& tag_YO; + +extern const XmlTag& tag_faces; +extern const XmlTag& tag_axes; + +extern const XmlTag& tag_Origin; +extern const XmlTag& tag_Direction; + +extern const XmlTag& tag_Min; +extern const XmlTag& tag_Max; + +extern const XmlTag& tag_WebTable; +extern const XmlTag& tag_WebList; +extern const XmlTag& tag_WebEntry; +extern const XmlTag& tag_WebKey; +extern const XmlTag& tag_WebValue; +extern const XmlTag& tag_WebType; + +extern const XmlTag& tag_customPhysProp; +extern const XmlTag& tag_customDensity; +extern const XmlTag& tag_customFriction; +extern const XmlTag& tag_customElasticity; +extern const XmlTag& tag_customFrictionWeight; +extern const XmlTag& tag_customElasticityWeight; + + + +// TODO: Optimization: Create a base class that has not children or attributes + +class XmlElement; +class XmlWriter; + +class XmlNameValuePair +{ +public: + +#ifndef _WIN32 +#ifdef UINT +#undef UINT +#endif +#endif + + typedef enum { NONE, NAME, STRING, CONTENTID, BOOL, INT, UINT, FLOAT, HANDLE, DOUBLE } ValueType; + +private: + const XmlTag& tag; + mutable ValueType valueType; + union { + mutable std::string* stringValue; + mutable RBX::ContentId* contentIdValue; + mutable bool boolValue; + mutable int intValue; + mutable unsigned int uintValue; + mutable float floatValue; + mutable double doubleValue; + mutable const RBX::Name* nameValue; + mutable RBX::InstanceHandle* handleValue; // TODO: with in-place constructor/destructor, could we avoid a "new" + }; + + void clearValue() const; + +public: + XmlNameValuePair(const XmlTag& tag) + :tag(tag),valueType(NONE) {} + XmlNameValuePair(const XmlTag& tag, const std::string& text) + :tag(tag),stringValue(new std::string(text)),valueType(STRING) {} + XmlNameValuePair(const XmlTag& tag, const char* text) + :tag(tag),stringValue(new std::string(text)),valueType(STRING) {} + XmlNameValuePair(const XmlTag& tag, RBX::ContentId contentId) + :tag(tag),contentIdValue(new RBX::ContentId(contentId)),valueType(CONTENTID) {} + XmlNameValuePair(const XmlTag& tag, const int _number) + :tag(tag),valueType(INT),intValue(_number) {} + XmlNameValuePair(const XmlTag& tag, const unsigned int _number) + :tag(tag),valueType(UINT),uintValue(_number) {} + XmlNameValuePair(const XmlTag& tag, const RBX::Name* value) + :tag(tag),valueType(NAME),nameValue(value) {} + XmlNameValuePair(const XmlTag& tag, bool value) + :tag(tag),valueType(BOOL),boolValue(value) {} + XmlNameValuePair(const XmlTag& tag, float value) + :tag(tag),valueType(FLOAT),floatValue(value) {} + XmlNameValuePair(const XmlTag& tag, double value) + :tag(tag),valueType(DOUBLE),doubleValue(value) {} + XmlNameValuePair(const XmlTag& tag, RBX::InstanceHandle value) + :tag(tag),valueType(HANDLE),handleValue(new RBX::InstanceHandle(value)) {} + ~XmlNameValuePair() { + clearValue(); + } + const XmlTag& getTag() const {return tag;} + + bool isValueEmpty() const { return valueType==NONE; } + + // equality tests (does not change valueType + bool isValueEqual(const std::string& value) const; + bool isValueEqual(RBX::ContentId contentId) const; + bool isValueEqual(int value) const; + bool isValueEqual(float value) const; + bool isValueEqual(double value) const; + bool isValueEqual(bool value) const; + bool isValueEqual(const RBX::Name* value) const; + bool isValueEqual(RBX::InstanceHandle value) const; + // Templated version that can be implemented by clients + template + bool isValueEqual(T value) const; + + std::string toString(XmlWriter* writer) const; + + // returns true if the value is of the given type + template + bool isValueType() const; + ValueType getValueType() const {return valueType;} + + // get requests. If possible, these functions will convert valueType to the desired type + // TODO: refactor to "toValue"? + bool getValue(std::string &value) const; + bool getValue(RBX::ContentId& contentId) const; + bool getValue(int &value) const; + bool getValue(unsigned int &value) const; + bool getValue(float &value) const; + bool getValue(double &value) const; + bool getValue(bool &value) const; + bool getValue(const RBX::Name* &value) const; + bool getValue(RBX::InstanceHandle &value) const; + // Templated version that can be implemented by clients + template + bool getValue(T& value) const; + + void setValue(std::string value) {clearValue(); stringValue = new std::string(value); valueType=STRING; } + void setValue(RBX::ContentId contentId) {clearValue(); contentIdValue = new RBX::ContentId(contentId); valueType=CONTENTID; } + void setValue(const char* value) {clearValue(); stringValue = new std::string(value); valueType=STRING; } + void setValue(int value) {clearValue(); intValue = value; valueType=INT; } + void setValue(unsigned int value) {clearValue(); uintValue = value; valueType=UINT; } + void setValue(bool value) {clearValue(); boolValue = value; valueType=BOOL; }; + void setValue(float value) {clearValue(); floatValue = value; valueType=FLOAT; }; + void setValue(double value) {clearValue(); doubleValue = value; valueType=DOUBLE; }; + void setValue(const RBX::Name* value) {clearValue(); nameValue = value; valueType=NAME; }; + void setValue(RBX::InstanceHandle handle) {clearValue(); handleValue = new RBX::InstanceHandle(handle); valueType=HANDLE; }; + // Templated version that can be implemented by clients + template + void setValue(T value); +}; + +namespace RBX +{ + // Parent and Child are a variation on the left-child/right-sibling tree, where each node also contains a reference + // the its right-most child. This makes it fast to push a child at the right-most end + template + class Parent { + private: + ChildClass* first; // "left child" + ChildClass* last; // "right child" + + public: + Parent() + :first(NULL) + ,last(NULL) + {} + + void pushBackChild(ChildClass* child) { + if (last==NULL) + first = child; + else + last->setNextSibling(child); + last = child; + } + + void pushFrontChild(ChildClass* child) { + if (first==NULL) + last = child; + else + child->setNextSibling(first); + first = child; + } + + void addChild(ChildClass* child) { + pushBackChild(child); + } + + void removeChild(ChildClass* child) { + if (first==child) + first = child->nextSibling(); + else { + for (ChildClass* sibling = first; sibling!=NULL; sibling = sibling->nextSibling()) { + if (sibling->nextSibling()==child) { + sibling->setNextSibling(child->nextSibling()); + if (child==last) + last = sibling; + break; + } + } + } + child->setNextSibling(NULL); + } + + ChildClass* firstChild() { return first; } + ChildClass* nextChild(ChildClass* child) { return child->nextSibling(); } + const ChildClass* firstChild() const { return first; } + const ChildClass* nextChild(const ChildClass* child) const { return child->nextSibling(); } + }; + + + template + class Sibling { + private: + SiblingClass* next; // "right sibling" + protected: + Sibling():next(NULL) {} + Sibling(SiblingClass* nextSibling):next(nextSibling) {} + public: + const SiblingClass* nextSibling() const { return next; } + SiblingClass* nextSibling() { return next; } + private: + friend class Parent; // Only give "ParentClass" access to the setting of the sibling + void setNextSibling(SiblingClass* nextSibling) { this->next = nextSibling; } + }; +} + +class XmlAttribute + : public RBX::Sibling + , public XmlNameValuePair + , public RBX::Allocator +{ +public: + XmlAttribute(const XmlTag& tag) + :XmlNameValuePair(tag) {} + template + XmlAttribute(const XmlTag& tag, T value) + :XmlNameValuePair(tag, value) {} +}; + +class XmlElement + : public RBX::Sibling + , public RBX::Parent + , public XmlNameValuePair + , public RBX::Allocator +{ +#ifdef _DEBUG + char leak[15]; + void recordLeak() + { + strcpy(leak,"XmlElement"); + } +#endif +private: + RBX::Parent attributes; + +public: + XmlElement(const XmlTag& tag) + :XmlNameValuePair(tag) + { +#ifdef _DEBUG + recordLeak(); +#endif + } + template + XmlElement(const XmlTag& tag, T value) + :XmlNameValuePair(tag, value) + { +#ifdef _DEBUG + recordLeak(); +#endif + } + + ~XmlElement() { + XmlAttribute* attr = attributes.firstChild(); + while (attr) + { + XmlAttribute* temp = attr; + attr = attr->nextSibling(); + delete temp; + } + + XmlElement* child = firstChild(); + while (child) + { + XmlElement* temp = child; + child = child->nextSibling(); + delete temp; + } + } + + + ///////////////////////////////////////////////////////////////////// + // attributes + // + + // returns true if this element has an xsi:nil attribute with value "true" + bool isXsiNil() const; + + template + inline void addAttribute(const XmlTag& _tag, T value) { + XmlAttribute* a = new XmlAttribute(_tag, value); + addAttribute(a); + } + + inline const XmlAttribute* getFirstAttribute() const {return attributes.firstChild();} + inline const XmlAttribute* getNextAttribute(const XmlAttribute* attribute) const {return attributes.nextChild(attribute);} + inline XmlAttribute* getFirstAttribute() {return attributes.firstChild();} + inline XmlAttribute* getNextAttribute(XmlAttribute* attribute) {return attributes.nextChild(attribute);} + + const XmlAttribute* findAttribute(const XmlTag& _tag) const; + XmlAttribute* findAttribute(const XmlTag& _tag); + inline bool findAttributeValue(const XmlTag& _tag, const RBX::Name*& value) const { + const XmlAttribute* attribute = findAttribute(_tag); + if (attribute==NULL) + return false; + return attribute->getValue(value); + } + inline bool findAttributeValue(const XmlTag& _tag, std::string& value) const { + const XmlAttribute* attribute = findAttribute(_tag); + if (attribute==NULL) + return false; + return attribute->getValue(value); + } + + ///////////////////////////////////////////////////////////////////// + // child elements + // + inline XmlElement* addChild(XmlElement* element) { pushBackChild(element); return element; } + XmlElement* addChild(const XmlTag& _tag) { + XmlElement* n = new XmlElement(_tag); + return addChild(n); + } + + const XmlElement* findFirstChildByTag(const XmlTag& _tag) const; + const XmlElement* findNextChildWithSameTag(const XmlElement* node) const; + +protected: + inline void addAttribute(XmlAttribute* attribute) { + attributes.pushBackChild(attribute); + } + +}; + + +#endif \ No newline at end of file diff --git a/App/v8xml/XmlSerializer.h b/App/v8xml/XmlSerializer.h new file mode 100644 index 0000000..4ed0490 --- /dev/null +++ b/App/v8xml/XmlSerializer.h @@ -0,0 +1,107 @@ +#ifndef V8XML_XMLSERIALIZER_H +#define V8XML_XMLSERIALIZER_H + +#include +#include +#include + +#include + +#include "V8Xml/XmlElement.h" + +namespace RBX +{ + class ContentProvider; +} +class RBXBaseClass XmlWriter : public boost::noncopyable { + +protected: + std::map handles; + typedef boost::unordered_map IdValidationMap; + IdValidationMap idValidationMap; + std::ostream& stream; + XmlWriter(std::ostream& stream); +public: + virtual void serialize(const XmlElement* xmlNode) = 0; + + int getHandleIndex(RBX::InstanceHandle h) { + int i; + std::map::iterator iter = handles.find(h); + if (iter==handles.end()) { + i = static_cast(handles.size()); // TODO: Should this be size_t getHandleIndex? + handles[h] = i; + return i; + } else + i = iter->second; + return i; + } + + bool isValidId(const std::string& id, const RBX::InstanceHandle& h) const + { + IdValidationMap::const_iterator itr = idValidationMap.find(id); + return itr == idValidationMap.end() || (itr->second.getTarget() == h.getTarget()); + } + + void recordId(const std::string& id, const RBX::InstanceHandle& h) + { + RBXASSERT(isValidId(id, h)); + idValidationMap[id] = h; + } +}; + +class TextXmlWriter : public XmlWriter +{ +public: + TextXmlWriter(std::ostream& stream) + :XmlWriter(stream) + {} + void serialize(const XmlElement* xmlNode); +protected: + void serialize(const XmlElement* xmlNode, int depth); + void writeOpenTag(const XmlElement* element, int depth); + void writeCloseTag(const XmlElement* element, int depth); + +public: + static void xmlEncodedWrite(std::ostream& stream, const std::string& text); + static void xmlOrCDataEncodedWrite(std::ostream& stream, const std::string& text); +protected: + void serializeNode(const XmlElement* xmlNode, int depth); + +}; + +class XmlParser : public boost::noncopyable { + +protected: + std::streambuf* buffer; + XmlParser(std::streambuf* buffer); + std::stack elements; // stack +public: + virtual std::auto_ptr parse() = 0; +}; + +class TextXmlParser : public XmlParser { + + std::map legacyHashes; // workaround for a bug in the MD5 hasher +public: + TextXmlParser(std::streambuf* buffer) + :XmlParser(buffer) + {} + std::auto_ptr parse(); + +private: + void skipWhitespace(); + + std::string readTag(); + std::string readFirstTag(); + std::string readText(bool decode); + + // attribute handling + std::string removeTag(const std::string& contents, int &index); + std::string findNextToken(const std::string& contents, int &index); + std::string findText(const std::string& attribute); + XmlElement* parseAttributes(const std::string& currentTag); +}; + + + +#endif diff --git a/App/voxel/AreaCopy.h b/App/voxel/AreaCopy.h new file mode 100644 index 0000000..58ea53e --- /dev/null +++ b/App/voxel/AreaCopy.h @@ -0,0 +1,68 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "Voxel/Cell.h" +#include "Voxel/Region.h" +#include "Voxel/Water.h" + +#include +#include + +namespace RBX { namespace Voxel { + +// Helper object for tasks that need fast access to voxel cells across +// SpatialRegion boundaries. Keeps an internal buffer of cells, size determined +// by template parameters. Buffer can be refreshed from another voxel storage +// mechanism (or another AreaCopy) repeatedly. +template +class AreaCopy { + // Helper object to comply with the Region API + class Chunk { + static const int kSize = XDim * YDim * ZDim; + + std::vector cells; + std::vector materials; + Vector3int16 firstCellLocation; + bool isAllEmpty; + + bool contains(const Vector3int16& cellLoc) const; + void fillEmpty(const Vector3int16& minLoc, const Vector3int16& maxLoc); + template + void fillFromRegion(const RegionType& region); + + public: + // Chunk API + static const int kXOffsetMultiplier = 1; + static const int kYOffsetMultiplier = XDim * ZDim; + static const int kZOffsetMultiplier = XDim; + + static int kFaceDirectionToPointerOffset[7]; + static int voxelCoordOffsetToIndexOffset(const Vector3int16& offsetCoord); + int voxelCoordToArrayIndex(const Vector3int16& globalCoord) const; + const std::vector& getConstData() const; + const std::vector& getConstMaterial() const; + void fillLocalAreaInfo(const Vector3int16& loc, + const Water::RelevantNeighbors& neighbors, Water::LocalAreaInfo* out) + const; + + // other methods + template + void loadData(const Source* source, const Vector3int16& firstCellLocation); + bool getIsAllEmpty() const; + }; + + Chunk storage; + +public: + typedef RBX::Voxel::Region Region; + static const Region kStaticEndRegion; + + Region getRegion(const Vector3int16& minCoords, const Vector3int16& maxCoords) const; + + template + void loadData(const Source* source, const Vector3int16& rootCell); +}; + +} } + +#include "AreaCopy.inl" diff --git a/App/voxel/AreaCopy.inl b/App/voxel/AreaCopy.inl new file mode 100644 index 0000000..f9e7746 --- /dev/null +++ b/App/voxel/AreaCopy.inl @@ -0,0 +1,184 @@ +#pragma once + +namespace RBX { namespace Voxel { + +template +bool AreaCopy::Chunk::contains(const Vector3int16& cellLocation) const { + return cellLocation.isBetweenInclusive(firstCellLocation, + firstCellLocation + Vector3int16(XDim, YDim, ZDim) - Vector3int16::one()); +} + +template +void AreaCopy::Chunk::fillEmpty( + const Vector3int16& minLoc, const Vector3int16& maxLoc) { + + unsigned int xWidth = maxLoc.x - minLoc.x + 1; + RBXASSERT((xWidth & 0x1) == 0); + + Vector3int16 counter; + for (counter.y = minLoc.y; counter.y <= maxLoc.y; ++counter.y) { + for (counter.z = minLoc.z; counter.z <= maxLoc.z; ++counter.z) { + counter.x = minLoc.x; + unsigned int index = voxelCoordToArrayIndex(counter); + memset(&cells[index], + Cell::convertToUnsignedCharForFile(Constants::kUniqueEmptyCellRepresentation), + xWidth * sizeof(Cell)); + // technically material doesn't need to be set for empty cells + memset(&materials[index / 2], 0xff, xWidth / 2); + } + } +} + +template +template +void AreaCopy::Chunk::fillFromRegion(const RegionType& region) { + for (typename RegionType::xline_iterator itr = region.xLineBegin(); + itr != region.xLineEnd(); ++itr) { + const size_t lineSize = itr.getLineSize(); + RBXASSERT(contains(itr.getCurrentLocation())); + RBXASSERT(contains(itr.getCurrentLocation() + Vector3int16(lineSize - 1, 0, 0))); + + unsigned int index = voxelCoordToArrayIndex(itr.getCurrentLocation()); + if (lineSize == 32) { + memcpy(&cells[index], itr.getLineCells(), 32 * sizeof(Cell)); + memcpy(&materials[index/2], itr.getLineMaterials(), 32 / 2); + } else { + const Cell* cellSrc = itr.getLineCells(); + const unsigned char* materialSrc = itr.getLineMaterials(); + for (size_t i = 0; i < lineSize; ++i) { + cells[index + i] = cellSrc[i]; + materials[(index + i) / 2] = materialSrc[i / 2]; + } + } + } +} + +template +int AreaCopy::Chunk::kFaceDirectionToPointerOffset[7] = { + 1, + XDim, + -1, + -((int)XDim), + XDim * ZDim, + -(int)(XDim * ZDim), + 0 +}; + +template +int AreaCopy::Chunk::voxelCoordOffsetToIndexOffset( + const Vector3int16& localCoord) { + return localCoord.x + (XDim * localCoord.z) + (XDim * ZDim * localCoord.y); +} + +template +int AreaCopy::Chunk::voxelCoordToArrayIndex( + const Vector3int16& globalCoord) const { + return voxelCoordOffsetToIndexOffset(globalCoord - firstCellLocation); +} + +template +const std::vector& AreaCopy::Chunk::getConstData() const { + return cells; +} + +template +const std::vector& AreaCopy::Chunk::getConstMaterial() const { + return materials; +} + +template +void AreaCopy::Chunk::fillLocalAreaInfo( + const Vector3int16& globalCoord, + const Water::RelevantNeighbors& relevantNeighbors, + Water::LocalAreaInfo* out) const { + + RBXASSERT(contains(globalCoord)); + RBXASSERT(contains(globalCoord + relevantNeighbors.aboveNeighbor)); + RBXASSERT(contains(globalCoord + relevantNeighbors.primaryNeighbor)); + RBXASSERT(contains(globalCoord + relevantNeighbors.secondaryNeighbor)); + RBXASSERT(contains(globalCoord + relevantNeighbors.diagonalNeighbor)); + RBXASSERT(contains(globalCoord + relevantNeighbors.diagonalUpNeighbor)); + + unsigned int centerIndex = voxelCoordToArrayIndex(globalCoord); + + out->aboveNeighbor = cells[centerIndex + + voxelCoordOffsetToIndexOffset(relevantNeighbors.aboveNeighbor)]; + out->primaryNeighbor = cells[centerIndex + + voxelCoordOffsetToIndexOffset(relevantNeighbors.primaryNeighbor)]; + out->secondaryNeighbor = cells[centerIndex + + voxelCoordOffsetToIndexOffset(relevantNeighbors.secondaryNeighbor)]; + out->diagonalNeighbor = cells[centerIndex + + voxelCoordOffsetToIndexOffset(relevantNeighbors.diagonalNeighbor)]; + out->diagonalUpNeighbor = cells[centerIndex + + voxelCoordOffsetToIndexOffset(relevantNeighbors.diagonalUpNeighbor)]; +} + +template +template +void AreaCopy::Chunk::loadData(const Source* source, + const Vector3int16& rootCell) { + + if (cells.empty()) { + std::vector cellsSwap(kSize, Constants::kUniqueEmptyCellRepresentation); + std::vector materialsSwap((kSize + 1) / 2, 0xff); + cells.swap(cellsSwap); + materials.swap(materialsSwap); + } + + firstCellLocation = rootCell; + + const Vector3int16 maxCell = rootCell + + Vector3int16(XDim, YDim, ZDim) - Vector3int16::one(); + + const SpatialRegion::Id minRegion = + SpatialRegion::regionContainingVoxel(rootCell); + const SpatialRegion::Id maxRegion = + SpatialRegion::regionContainingVoxel(maxCell); + + isAllEmpty = true; + Vector3int16 regionIdCounter; + for (regionIdCounter.y = minRegion.value().y; regionIdCounter.y <= maxRegion.value().y; ++regionIdCounter.y) { + for (regionIdCounter.z = minRegion.value().z; regionIdCounter.z <= maxRegion.value().z; ++regionIdCounter.z) { + for (regionIdCounter.x = minRegion.value().x; regionIdCounter.x <= maxRegion.value().x; ++regionIdCounter.x) { + SpatialRegion::Id id(regionIdCounter); + Region3int16 extents = SpatialRegion::inclusiveVoxelExtentsOfRegion(id); + const Vector3int16 queryMin = extents.getMinPos().max(rootCell); + const Vector3int16 queryMax = extents.getMaxPos().min(maxCell); + + // for material alignment issues, all x segments must be even, and + // start from an even location in the region + RBXASSERT(((queryMax.x - queryMin.x + 1) & 0x1) == 0); + RBXASSERT(((queryMin.x - firstCellLocation.x) & 0x1) == 0); + + typename Source::Region region = source->getRegion(queryMin, queryMax); + if (region.isGuaranteedAllEmpty()) { + fillEmpty(queryMin, queryMax); + } else { + isAllEmpty = false; + fillFromRegion(region); + } + } + } + } +} + +template +bool AreaCopy::Chunk::getIsAllEmpty() const { + return isAllEmpty; +} + + +template +typename AreaCopy::Region AreaCopy::getRegion( + const Vector3int16& minCoords, const Vector3int16& maxCoords) const { + return Region(storage.getIsAllEmpty() ? NULL : &storage, minCoords, maxCoords); +} + +template +template +void AreaCopy::loadData(const Source* source, + const Vector3int16& rootCell) { + storage.loadData(source, rootCell); +} + +} } diff --git a/App/voxel/Cell.h b/App/voxel/Cell.h new file mode 100644 index 0000000..0c4eb25 --- /dev/null +++ b/App/voxel/Cell.h @@ -0,0 +1,206 @@ +#pragma once + +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +// Defines voxels at the cellular and sub-cellular level + +namespace RBX { namespace Voxel { + +enum CellMaterial +{ + CELL_MATERIAL_Deprecated_Empty = 0, + CELL_MATERIAL_Grass = 1, + CELL_MATERIAL_Sand = 2, + CELL_MATERIAL_Brick = 3, + CELL_MATERIAL_Granite = 4, + CELL_MATERIAL_Asphalt = 5, + CELL_MATERIAL_Iron = 6, + CELL_MATERIAL_Aluminum = 7, + CELL_MATERIAL_Gold = 8, + CELL_MATERIAL_Wood_Plank = 9, + CELL_MATERIAL_Wood_Log = 10, + CELL_MATERIAL_Gravel = 11, + CELL_MATERIAL_Cinder_Block = 12, + CELL_MATERIAL_Stone_Block = 13, + CELL_MATERIAL_Cement = 14, + CELL_MATERIAL_Red_Plastic = 15, + CELL_MATERIAL_Blue_Plastic = 16, + CELL_MATERIAL_Water = 17, + CELL_MATERIAL_Unspecified = 255, + MAX_CELL_MATERIALS = 18, +}; + +enum CellBlock +{ + CELL_BLOCK_Solid = 0, + CELL_BLOCK_VerticalWedge = 1, + CELL_BLOCK_CornerWedge = 2, + CELL_BLOCK_InverseCornerWedge = 3, + CELL_BLOCK_HorizontalWedge = 4, + + // Enum values below this line are intentionally not reflected! + // Talk with dignatoff@ before exposing these enums! + CELL_BLOCK_Empty = 5, + + //InverseVerticalWedge = 4, + //TopCornerWedge = 5, + MAX_CELL_BLOCKS = 8, +}; + +// Viewed from a downwards vertical perspective, +// orientation defines the corner that the block starts in in a clockwise fashion +enum CellOrientation +{ + CELL_ORIENTATION_NegZ = 0, // upper left + CELL_ORIENTATION_X = 1, // upper right + CELL_ORIENTATION_Z = 2, // lower right + CELL_ORIENTATION_NegX = 3, // lower left + MAX_CELL_ORIENTATIONS = 4 +}; + +enum WaterCellForce +{ + WATER_CELL_FORCE_None = 0, + WATER_CELL_FORCE_Small = 1, + WATER_CELL_FORCE_Medium = 2, + WATER_CELL_FORCE_Strong = 3, + WATER_CELL_FORCE_MaxForce = 4, + MAX_WATER_CELL_FORCES = 5 +}; + +enum WaterCellDirection +{ + WATER_CELL_DIRECTION_NegX = 0, + WATER_CELL_DIRECTION_X = 1, + WATER_CELL_DIRECTION_NegY = 2, + WATER_CELL_DIRECTION_Y = 3, + WATER_CELL_DIRECTION_NegZ = 4, + WATER_CELL_DIRECTION_Z = 5, + MAX_WATER_CELL_DIRECTIONS = 6 +}; + +class SolidTerrainCell { + // Material used to be stored in solid terrain voxel; it has + // subsequently been moved to a completely separate storage mechanism. + // The field is here to preserve memory layout with the earlier version. + unsigned char DEPRECATED_material : 3; + unsigned char block : 3; + unsigned char orientation : 2; + +public: + CellBlock getBlock() const { return (CellBlock) block; } + CellOrientation getOrientation() const { + return (CellOrientation) orientation; + } + + void setBlock(CellBlock block) { this->block = block; } + void setOrientation(CellOrientation orientation) { + this->orientation = orientation; + } +}; + +class WaterCell { + // Water voxels are implemented to be bit-compatible with solid voxels, + // so this bit layout matches the bit layout of SolidTerrainCell. + unsigned char dataPart2 : 3; + unsigned char blockMustBeEmpty : 3; + unsigned char dataPart1 : 2; + + unsigned int getWaterData() const { + return ((dataPart1 << 3) | dataPart2) - 1; + } +public: + WaterCellForce getForce() const { + return (WaterCellForce) ((getWaterData() / MAX_WATER_CELL_DIRECTIONS) % MAX_WATER_CELL_FORCES); + } + WaterCellDirection getDirection() const { + return (WaterCellDirection) (getWaterData() % MAX_WATER_CELL_DIRECTIONS); + } + + void setForceAndDirection(WaterCellForce force, WaterCellDirection direction) { + unsigned int rawData = (force * MAX_WATER_CELL_DIRECTIONS + direction) + 1; + dataPart2 = rawData & 0x7; + dataPart1 = (rawData >> 3) & 0x3; + } +}; + +// Data structure that represents one voxel cell. The cell can either be water or solid terrain, +// so this class is a union of WaterCell and SolidTerrainCell. +union Cell { + SolidTerrainCell solid; + WaterCell water; + + Cell() { + // There isn't a simple way to make sure all of the members are + // zero by going through setter methods. SolidTerrainCell, for example, + // has no way to control the bits in DEPRECATED_material. + // Performance testing has shown this to not be a perf hit compared to + // casting this to unsigned char* and setting that to zero. + memset(this, 0, sizeof(Cell)); + } + + // True if the voxel is completely empty (no solid terrain and no water) + inline bool isEmpty() const; + + // Indicates if this cell has been set to water explicitly by the user. + // Note that water can also exist in wedge cells. Use Region and/or + // Region::iterator methods for a way to detect all kinds of water + // simultaneously. + inline bool isExplicitWaterCell() const { return !isEmpty() && solid.getBlock() == CELL_BLOCK_Empty; } + + inline bool operator==(const Cell& other) const { + return ((const unsigned char*)this)[0] == ((const unsigned char*)&other)[0]; + } + inline bool operator!=(const Cell& other) const { + return !((*this) == other); + } + + // Convert to/from unsigned char, for networking and saving to file + static inline unsigned char serializeAsUnsignedChar(const Cell v) { + return ((unsigned char*)&v)[0]; + } + static inline Cell deserializeFromUnsignedChar(unsigned char cell) { + return ((Cell*)&cell)[0]; + } + static inline unsigned char convertToUnsignedCharForFile(const Cell v) { + return ((unsigned char*)&v)[0]; + } + static inline Cell readUnsignedCharFromFile(unsigned char cell) { + return ((Cell*)&cell)[0]; + } + // Old style voxel access. Avoid using these methods where possible. + static inline unsigned char asUnsignedCharForDeprecatedUses(const Cell v) { + return ((unsigned char*)&v)[0]; + } + static inline Cell readUnsignedCharFromDeprecatedUse(unsigned char cell) { + return ((Cell*)&cell)[0]; + } +}; + +BOOST_STATIC_ASSERT(sizeof(Cell) == 1); + +namespace Constants { + // There is exactly one way to represent an empty cell. This constant stores that representation. + extern const Cell kUniqueEmptyCellRepresentation; + // When there is water in a cell that has a solid wedge, the water state is always the same. This + // constant stores the water state for water-on-wedge cells. + extern const Cell kWaterOnWedgeCell; +} + +bool Cell::isEmpty() const { + return (*this) == Constants::kUniqueEmptyCellRepresentation; +} + +std::ostream& operator<<(std::ostream& os, const RBX::Voxel::Cell& v); + +const int kXZ_CHUNK_SIZE = 32; +const int kY_CHUNK_SIZE = 16; + +const int kCELL_SIZE = 4; +const int kHALF_CELL = kCELL_SIZE / 2; +const int kCELL_SIZE_AS_BIT_SHIFT = 2; + +} } + diff --git a/App/voxel/CellChangeListener.h b/App/voxel/CellChangeListener.h new file mode 100644 index 0000000..4725d8a --- /dev/null +++ b/App/voxel/CellChangeListener.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Voxel/Cell.h" + +namespace RBX { + +namespace Voxel { + +struct CellChangeInfo { + const Vector3int16 position; + + Cell beforeCell; + Cell afterCell; + bool hadWaterBefore; + bool hasWaterAfter; + CellMaterial afterMaterial; + + CellChangeInfo(const Vector3int16& position, + Cell beforeCell, Cell afterCell, + bool hadWaterBefore, bool hasWaterAfter, + CellMaterial afterMaterial) + : position(position) + , beforeCell(beforeCell) + , afterCell(afterCell) + , hadWaterBefore(hadWaterBefore) + , hasWaterAfter(hasWaterAfter) + , afterMaterial(afterMaterial) + { } +}; + +// Callback interface for components that want to be notified when terrain +// cells change +class CellChangeListener { +public: + virtual void terrainCellChanged(const CellChangeInfo& info) = 0; +}; + +} } diff --git a/App/voxel/ChunkMap.h b/App/voxel/ChunkMap.h new file mode 100644 index 0000000..757999f --- /dev/null +++ b/App/voxel/ChunkMap.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Util/SpatialRegion.h" + +#include +#include + +namespace RBX { namespace Voxel { + +// Associative container for mapping SpatialRegion::Id to a value type. +// ValueType should implement no-arg constructor and assignment operator. +template +class ChunkMap { + typedef boost::unordered_map ValueMap; + +public: + ChunkMap(); + + // Mutating accessor, will insert a new ValueType if the id wasn't already + // contained in this container. + ValueType& insert(const SpatialRegion::Id& id); + + // Constant accessor, returns NULL if id is not contained. + const ValueType* find(const SpatialRegion::Id& id) const; + ValueType* find(const SpatialRegion::Id& id); + + // Removes the key/value pair for the given id. Does nothing if the key + // is not present. + void erase(const SpatialRegion::Id& id); + + // Get all chunks + std::vector getChunks() const; + + // Get number of chunks + size_t size() const; + +private: + ValueMap values; +}; + +} } + +#include "ChunkMap.inl" diff --git a/App/voxel/ChunkMap.inl b/App/voxel/ChunkMap.inl new file mode 100644 index 0000000..52a24e4 --- /dev/null +++ b/App/voxel/ChunkMap.inl @@ -0,0 +1,51 @@ +#pragma once + +#include "Voxel/Cell.h" + +namespace RBX { namespace Voxel { + +template ChunkMap::ChunkMap() +{ +} + +template ValueType& ChunkMap::insert(const SpatialRegion::Id& id) +{ + return values[id]; +} + +template const ValueType* ChunkMap::find(const SpatialRegion::Id& id) const +{ + typename ValueMap::const_iterator it = values.find(id); + + return (it == values.end()) ? NULL : &it->second; +} + +template ValueType* ChunkMap::find(const SpatialRegion::Id& id) +{ + typename ValueMap::iterator it = values.find(id); + + return (it == values.end()) ? NULL : &it->second; +} + +template void ChunkMap::erase(const SpatialRegion::Id& id) +{ + values.erase(id); +} + +template std::vector ChunkMap::getChunks() const +{ + std::vector result; + result.reserve(values.size()); + + for (typename ValueMap::const_iterator it = values.begin(); it != values.end(); ++it) + result.push_back(it->first); + + return result; +} + +template size_t ChunkMap::size() const +{ + return values.size(); +} + +} } diff --git a/App/voxel/Grid.Chunk.h b/App/voxel/Grid.Chunk.h new file mode 100644 index 0000000..9771ef7 --- /dev/null +++ b/App/voxel/Grid.Chunk.h @@ -0,0 +1,73 @@ +#pragma once +// suffix header file for Grid.h + +#include "Util/SpatialRegion.h" +#include "Voxel/Water.h" + +namespace RBX { namespace Voxel { + +// Private storage structure supporting Grid. +// NOT TO BE USED ANYWHERE EXCEPT Grid.cpp AND Grid.h. +// Stores a contiguous 3-D box of terrain contents. Namespace contains +// constants and helper methods for accessing the data. +class Grid::Chunk { + +private: + + bool initialized; + unsigned int countOfNonEmptyCells; + std::vector data; + std::vector material; + const Grid* owner; + + static const int kXOffsetMultiplier = 1; + static const int kZOffsetMultiplier = + SpatialRegion::Constants::kRegionXDimensionInVoxels; + static const int kYOffsetMultiplier = + SpatialRegion::Constants::kRegionXDimensionInVoxels * + SpatialRegion::Constants::kRegionZDimensionInVoxels; + +public: + + static const int kFaceDirectionToPointerOffset[7]; + + static inline int voxelCoordOffsetToIndexOffset(const Vector3int16& offset) { + return (offset * Vector3int16(kXOffsetMultiplier, kYOffsetMultiplier, kZOffsetMultiplier)).sum(); + } + + static inline unsigned int voxelCoordToArrayIndex(const Vector3int16& coord) { + return voxelCoordOffsetToIndexOffset( + SpatialRegion::voxelCoordinateRelativeToEnclosingRegion(coord)); + } + + Chunk(); + ~Chunk(); + + // Initialization method. Safe to call multiple times. This object owns + // a significant amount of memory, so a separate init method was made to + // allow explicit control over when that memory is allocated. + void init(const Grid* owner); + + std::vector& getData() { return data; } + const std::vector& getConstData() const { return data; } + std::vector& getMaterial() { return material; } + const std::vector& getConstMaterial() const { return material; } + + void updateCountOfNonEmptyCells(int delta) { + countOfNonEmptyCells += delta; + RBXASSERT((int)(countOfNonEmptyCells) >= 0); + } + bool hasNoUsefulData() const { + return countOfNonEmptyCells == 0; + } + + // for water + void fillLocalAreaInfo(const Vector3int16& centerCoord, + const Water::RelevantNeighbors& relevantNeighbors, + Water::LocalAreaInfo* out) const { + return owner->fillLocalAreaInfo(centerCoord, relevantNeighbors, out); + } +}; + +} } + diff --git a/App/voxel/Grid.h b/App/voxel/Grid.h new file mode 100644 index 0000000..199cd64 --- /dev/null +++ b/App/voxel/Grid.h @@ -0,0 +1,76 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "Util/SpatialRegion.h" +#include "Voxel/CellChangeListener.h" +#include "Voxel/ChunkMap.h" +#include "Voxel/Region.h" +#include "Voxel/Water.h" + +#include +#include + +namespace RBX { namespace Voxel { + +// Storage class for terrain Voxels. Has methods for reading and writing +// voxels. The voxels for different areas of the terrain may be stored +// internally in separate sub-containers. Frequently allocates and re- +// allocates memory, and does not take any data model locks, so do not store +// VoxelRegions for later use (e.g. storing for a later job run). +class Grid { + class Chunk; + + typedef ChunkMap ChunkMapType; + ChunkMapType chunkMap; + unsigned int countOfNonEmptyCells; + + // Whenever a cell is changed, the cellChangedSignal will be notified. + // Note that this doesn't necessarily happen every time setCell is called: + // if setCell would set a cell to be the same value it currently it has, + // then the cellChangedSignal won't fire for that setCell call. + std::vector cellChangeListeners; + + const Cell& getVoxelLikelyThisChunk(const SpatialRegion::Id& id, + const Chunk& chunk, const Vector3int16& coord) const; + + void fillLocalAreaInfo(const Vector3int16& globalCoord, + const Water::RelevantNeighbors& neighbors, + Water::LocalAreaInfo* out) const; + +public: + typedef RBX::Voxel::Region Region; + + Grid(); + + // returns the number of cells in the terrain that are not empty + inline unsigned int getNonEmptyCellCount() const { return countOfNonEmptyCells; } + + // subscribe and unsubscribe from cell change events + void connectListener(CellChangeListener* listener); + void disconnectListener(CellChangeListener* listener); + + // Updates one cell. Will notify listeners of the cellChanged signal if the + // targeted cell is actually altered (new values != old values) after the + // new value is put in place. + void setCell(const Vector3int16& location, Cell newCell, + CellMaterial newMaterial); + + // Get a Cell region covering the extents specified. Does not support + // extents that span SpatialRegion boundaries. + Region getRegion(const Vector3int16& extent1, const Vector3int16& extent2) const; + + // Gets information about one cell + Cell getCell(const Vector3int16& pos) const; + CellMaterial getCellMaterial(const Vector3int16& pos) const; + Cell getWaterCell(const Vector3int16& pos) const; + + // Gets live chunk ids + std::vector getNonEmptyChunks() const; + std::vector getNonEmptyChunksInRegion(const Region3int16& extents) const; + + bool isAllocated() const; +}; + +} } + +#include "Voxel/Grid.Chunk.h" diff --git a/App/voxel/Region.h b/App/voxel/Region.h new file mode 100644 index 0000000..2a87476 --- /dev/null +++ b/App/voxel/Region.h @@ -0,0 +1,158 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "Voxel/Util.h" + +namespace RBX { namespace Voxel { + +// Read-only view of a contiguous, axis-aligned subsection of the entire voxel +// grid. +template +class Region { +public: + class iterator; + class xline_iterator; + + Region(); + Region(const InternalStorageType* internalStorage, + const Vector3int16& minCoords, const Vector3int16& maxCoords); + + // Returns true if all cells in this iteration are definitely empty. + // May return false if all cells are empty, but will never return true + // if some cells are set. + bool isGuaranteedAllEmpty() const; + + // returns true if the global coordinate is queryable in this region + bool contains(const Vector3int16& globalCoord) const; + + // methods for querying voxel related data for a global voxel coordinate. + inline const Cell& voxelAt(const Vector3int16& globalCoord) const; + inline CellMaterial materialAt(const Vector3int16& globalCoord) const; + inline bool hasWaterAt(const Vector3int16& globalCoord) const; + + // methods to make this an iterable container + iterator begin() const; + const iterator& end() const; + + xline_iterator xLineBegin() const; + const xline_iterator& xLineEnd() const; + + // Support methods + Region& operator=(const Region& other); + bool operator==(const Region& other) const; + +private: + static const Region kEndRegion; + static const iterator kEndIterator; + static const xline_iterator kEndXLineIterator; + + const InternalStorageType* internalStorage; + Vector3int16 minCoords; + Vector3int16 maxCoords; + + inline const Cell& voxelAtSkipAllEmptyCheck(const Vector3int16& globalCoord) const; + inline bool hasWaterAtSkipAllEmptyCheck(const Cell& cell, + const Vector3int16& globalCoord) const; + +}; + +// Iterator for accessing all voxels inside a Region sequentially. +// Iterates in Y-Z-X order (x axis is least significant ordered, y axis is +// most significant). +template +class Region::iterator { +private: + const Region& owningRegion; + + const Vector3int16 rangeSize; + unsigned int pointerSkipAtEndOfXLine; + unsigned int pointerSkipAtEndOfZLine; + + // Internal iteration counters + unsigned int xCounter, zCounter; + bool reachedEnd; + + // cached values (saved so that they aren't re-derived on each access) + Vector3int16 currentLocation; + unsigned int currentIndex; + const Cell* currentCell; + +public: + iterator(const Region& owningRegion); + + ////////////////////////////////////////////////// + // Reading data + + // Read at current location + inline const Vector3int16& getCurrentLocation() const; + inline const Cell& getCellAtCurrentLocation() const; + inline bool hasWaterAtCurrentLocation() const; + inline CellMaterial getMaterialAtCurrentLocation() const; + + // Read neighbors of current location. + // These methods should only be used when the caller knows that it is safe + // to query those cells (that the cells are contained in the Region) + inline const Cell& getNeighborCell(FaceDirection direction) const; + inline const Cell& getNeighborCell(FaceDirection direction1, FaceDirection direction2) const; + inline CellMaterial getNeighborMaterial(FaceDirection direction) const; + inline CellMaterial getNeighborMaterial(FaceDirection direction1, FaceDirection direction2) const; + inline const Cell& getArbitraryNeighborCell(const Vector3int16& neighborOffsets) const; + inline bool hasWaterAtNeighbor(const FaceDirection& direction) const; + + //////////////////////////////////////////// + // Normal iterator business + + // ++prefix form + inline iterator& operator++(); + + // operator== for terminating condition + inline bool operator==(const iterator& other); + inline bool operator!=(const iterator& other); +}; + +// Limited iterator. Iterates over the region, in increments equal to the +// X-Axis dimension of the region (aka over the the "xline"s of the region). +// Allows working with the entire line in one shot, to enhance performance of +// bulk operations like copying. +template +class Region::xline_iterator { + const Region& owningRegion; + const unsigned int lineSize; + const int minZ; + const unsigned int zDimSize; + const int maxY; + + unsigned int pointerSkipAtEndOfXLine; + unsigned int pointerSkipAtEndOfZLine; + + unsigned int zDimCounter; + + Vector3int16 currentLocation; + unsigned int currentIndex; + const Cell* currentCell; + bool reachedEnd; +public: + + xline_iterator(const Region& owningRegion); + + const Vector3int16& getCurrentLocation() const; + unsigned int getLineSize() const; + + // Returns a contiguous array of Cells that has exactly lineSize elements. + // The first cell corresponds to the current location, and progress along + // the positive X axis. + const Cell* getLineCells() const; + // Returns a contiguous array of unsigned char with exactly lineSize/2 + // elements. This is half-byte material information for the x line. + const unsigned char* getLineMaterials() const; + + bool operator==(const xline_iterator& other) const; + bool operator!=(const xline_iterator& other) const; + inline xline_iterator& operator++(); +}; + +} } + +#include "Voxel/Region.inl" +#include "Voxel/Region.iterator.inl" +#include "Voxel/Region.xline_iterator.inl" diff --git a/App/voxel/Region.inl b/App/voxel/Region.inl new file mode 100644 index 0000000..f44a542 --- /dev/null +++ b/App/voxel/Region.inl @@ -0,0 +1,134 @@ +#pragma once + +#include "Voxel/Water.h" + +///////////////////////////////////////////////////// +// template implementation file for Region.h + +namespace RBX { namespace Voxel { + +template +const Region Region::kEndRegion(NULL, Vector3int16::one(), Vector3int16::zero()); + +template +const typename Region::iterator Region::kEndIterator(Region::kEndRegion); + +template +const typename Region::xline_iterator Region::kEndXLineIterator(Region::kEndRegion); + +template +Region::Region() : + internalStorage(NULL), minCoords(Vector3int16::zero()), maxCoords(Vector3int16::zero()) {} + +template +Region::Region(const InternalStorageType* internalStorage, + const Vector3int16& minCoords, const Vector3int16& maxCoords) : + internalStorage(internalStorage), minCoords(minCoords), maxCoords(maxCoords) {} + +template +bool Region::isGuaranteedAllEmpty() const { + return internalStorage == NULL; +} + +template +bool Region::contains(const Vector3int16& globalCoord) const { + return globalCoord.isBetweenInclusive(minCoords, maxCoords); +} + +template +const Cell& Region::voxelAt( + const Vector3int16& globalCoord) const { + RBXASSERT_SLOW(contains(globalCoord)); + + if (isGuaranteedAllEmpty()) { + return Constants::kUniqueEmptyCellRepresentation; + } else { + return voxelAtSkipAllEmptyCheck(globalCoord); + } +} + +template +CellMaterial Region::materialAt( + const Vector3int16& globalCoord) const { + RBXASSERT_SLOW(contains(globalCoord)); + + if (isGuaranteedAllEmpty()) { + return CELL_MATERIAL_Water; + } else { + unsigned int index = internalStorage->voxelCoordToArrayIndex(globalCoord); + return readMaterial(&internalStorage->getConstMaterial()[0], + index, internalStorage->getConstData()[index]); + } +} + +template +bool Region::hasWaterAt( + const Vector3int16& globalCoord) const { + RBXASSERT_SLOW(contains(globalCoord)); + + if (isGuaranteedAllEmpty()) { + return false; + } else { + return hasWaterAtSkipAllEmptyCheck( + voxelAtSkipAllEmptyCheck(globalCoord), globalCoord); + } +} + +template +typename Region::iterator +Region::begin() const { + return iterator(*this); +} + +template +const typename Region::iterator& +Region::end() const { + return kEndIterator; +} + +template +typename Region::xline_iterator +Region::xLineBegin() const { + return xline_iterator(*this); +} + +template +const typename Region::xline_iterator& +Region::xLineEnd() const { + return kEndXLineIterator; +} + +template +Region& Region::operator=( + const Region& other) { + internalStorage = other.internalStorage; + minCoords = other.minCoords; + maxCoords = other.maxCoords; + return *this; +} + +template +bool Region::operator==( + const Region& other) const { + return internalStorage == other.internalStorage && + minCoords == other.minCoords && + maxCoords == other.maxCoords; +} + +template +const Cell& Region::voxelAtSkipAllEmptyCheck( + const Vector3int16& globalCoord) const { + RBXASSERT_SLOW(!isGuaranteedAllEmpty()); + return internalStorage->getConstData()[ + internalStorage->voxelCoordToArrayIndex(globalCoord)]; +} + +template +bool Region::hasWaterAtSkipAllEmptyCheck( + const Cell& cell, + const Vector3int16& globalCoord) const { + RBXASSERT_SLOW(!isGuaranteedAllEmpty()); + return Water::cellHasWater(internalStorage, cell, globalCoord); +} + +} } diff --git a/App/voxel/Region.iterator.inl b/App/voxel/Region.iterator.inl new file mode 100644 index 0000000..a93dede --- /dev/null +++ b/App/voxel/Region.iterator.inl @@ -0,0 +1,194 @@ +#pragma once + +// +// Implementation file for Region.iterator + +namespace RBX { namespace Voxel { + +namespace VoxelIteratorConstants { + const Vector3int16 kFaceDirectionToLocationOffset[6] = + { + Vector3int16( 1, 0, 0), + Vector3int16( 0, 0, 1), + Vector3int16(-1, 0, 0), + Vector3int16( 0, 0,-1), + Vector3int16( 0, 1, 0), + Vector3int16( 0,-1, 0), + }; +} + +template +Region::iterator::iterator( + const Region& owningRegion) : + owningRegion(owningRegion), + rangeSize((owningRegion.maxCoords - owningRegion.minCoords) + Vector3int16::one()), + xCounter(0), zCounter(0), reachedEnd(false) { + + // read min and max coord from owning region to simplify constructor logic + Vector3int16 minCoord = owningRegion.minCoords; + Vector3int16 maxCoord = owningRegion.maxCoords; + + if (!owningRegion.isGuaranteedAllEmpty()) { + // For speed, this implementation keeps a pointer to the current voxel. + // In order to implement operator++, we want to keep a "carriage return" + // pointer offset, for when the pointer needs to go from the end of + // an x line to the beginning of the x line in the next z line, and + // another offset for when the pointer needs to go from the end of + // an x-z plane to the beginning of the plane in the next y level. + + // The "carriage return" offset for the end of an x line should be + // zero in the degenerate case where the z dimension is 1. + + // skip at end of x line: (minX,minY,minZ+1) - (maxX,minY,minZ) + pointerSkipAtEndOfXLine = 0; + if (rangeSize.z > 1) { + pointerSkipAtEndOfXLine = + owningRegion.internalStorage->voxelCoordToArrayIndex( + Vector3int16(minCoord.x, minCoord.y, minCoord.z + 1)) - + owningRegion.internalStorage->voxelCoordToArrayIndex( + Vector3int16(maxCoord.x, minCoord.y, minCoord.z)); + } + + // skip at end of x-z plane: (minX,minY+1,minZ) - (maxX,minY,maxZ) + pointerSkipAtEndOfZLine = 0; + if (rangeSize.y > 1) { + pointerSkipAtEndOfZLine = + owningRegion.internalStorage->voxelCoordToArrayIndex( + Vector3int16(minCoord.x, minCoord.y + 1, minCoord.z)) - + owningRegion.internalStorage->voxelCoordToArrayIndex( + Vector3int16(maxCoord.x, minCoord.y, maxCoord.z)); + } + + currentLocation = minCoord; + currentIndex = owningRegion.internalStorage->voxelCoordToArrayIndex(currentLocation); + currentCell = &owningRegion.internalStorage->getConstData()[currentIndex]; + reachedEnd = currentLocation.y > maxCoord.y; + } else { + reachedEnd = true; + } +} + +template +const Vector3int16& Region::iterator::getCurrentLocation() const { + return currentLocation; +} + +template +const Cell& Region::iterator::getCellAtCurrentLocation() const { + return *currentCell; +} + +template +bool Region::iterator::hasWaterAtCurrentLocation() const { + return owningRegion.hasWaterAtSkipAllEmptyCheck(*currentCell, currentLocation); +} + +template +CellMaterial Region::iterator::getMaterialAtCurrentLocation() const { + return (CellMaterial)readMaterial( + &owningRegion.internalStorage->getConstMaterial()[0], currentIndex, *currentCell); +} + +template +const Cell& Region::iterator::getNeighborCell( + FaceDirection direction) const { + RBXASSERT_SLOW(owningRegion.contains(currentLocation + + kFaceDirectionToLocationOffset[direction])); + return currentCell[ + InternalStorageType::kFaceDirectionToPointerOffset[direction]]; +} + +template +const Cell& Region::iterator::getNeighborCell( + FaceDirection direction1, FaceDirection direction2) const { + RBXASSERT_SLOW(owningRegion.contains(currentLocation + + kFaceDirectionToLocationOffset[direction1] + kFaceDirectionToLocationOffset[direction2])); + return currentCell[ + InternalStorageType::kFaceDirectionToPointerOffset[direction1] + InternalStorageType::kFaceDirectionToPointerOffset[direction2]]; +} + +template +CellMaterial Region::iterator::getNeighborMaterial( + FaceDirection direction) const { + RBXASSERT_SLOW(owningRegion.contains(currentLocation + + kFaceDirectionToLocationOffset[direction])); + const int offset(InternalStorageType::kFaceDirectionToPointerOffset[direction]); + return (CellMaterial)readMaterial(&owningRegion.internalStorage->getConstMaterial()[0], + currentIndex + offset, currentCell[offset]); +} + +template +CellMaterial Region::iterator::getNeighborMaterial( + FaceDirection direction1, FaceDirection direction2) const { + RBXASSERT_SLOW(owningRegion.contains(currentLocation + + kFaceDirectionToLocationOffset[direction1] + kFaceDirectionToLocationOffset[direction2)); + const int offset(InternalStorageType::kFaceDirectionToPointerOffset[direction1] + InternalStorageType::kFaceDirectionToPointerOffset[direction2]); + return (CellMaterial)readMaterial(&owningRegion.internalStorage->getConstMaterial()[0], + currentIndex + offset, currentCell[offset]); +} + + +template +const Cell& Region::iterator::getArbitraryNeighborCell( + const Vector3int16& neighborOffsets) const { + RBXASSERT_SLOW(owningRegion.contains(currentLocation + neighborOffsets)); + return currentCell[ + InternalStorageType::voxelCoordOffsetToIndexOffset(neighborOffsets)]; +} + +template +bool Region::iterator::hasWaterAtNeighbor( + const FaceDirection& direction) const { + RBXASSERT_SLOW(owningRegion.contains(currentLocation + + kFaceDirectionToLocationOffset[direction])); + return owningRegion.hasWaterAtSkipAllEmptyCheck( + currentCell[InternalStorageType::kFaceDirectionToPointerOffset[direction]], + currentLocation + + VoxelIteratorConstants::kFaceDirectionToLocationOffset[direction]); +} + +template +typename Region::iterator& +Region::iterator::operator++() { + ++xCounter; + if (xCounter == rangeSize.x) { + xCounter = 0; + ++zCounter; + if (zCounter == rangeSize.z) { + zCounter = 0; + currentLocation.x = owningRegion.minCoords.x; + ++currentLocation.y; + currentLocation.z = owningRegion.minCoords.z; + currentIndex += pointerSkipAtEndOfZLine; + currentCell += pointerSkipAtEndOfZLine; + } else { + currentLocation.x = owningRegion.minCoords.x; + ++currentLocation.z; + currentIndex += pointerSkipAtEndOfXLine; + currentCell += pointerSkipAtEndOfXLine; + } + } else { + ++currentIndex; + ++currentCell; + ++currentLocation.x; + } + + reachedEnd = currentLocation.y > owningRegion.maxCoords.y; + return *this; +} + +template +bool Region::iterator::operator==(const iterator& other) { + if (reachedEnd || other.reachedEnd) { + return reachedEnd == other.reachedEnd; + } + return owningRegion == other.owningRegion && + currentLocation == other.currentLocation; +} + +template +bool Region::iterator::operator!=(const iterator& other) { + return !(this->operator==(other)); +} + +} } diff --git a/App/voxel/Region.xline_iterator.inl b/App/voxel/Region.xline_iterator.inl new file mode 100644 index 0000000..3856001 --- /dev/null +++ b/App/voxel/Region.xline_iterator.inl @@ -0,0 +1,96 @@ +#pragma once + +namespace RBX { namespace Voxel { + +template +Region::xline_iterator::xline_iterator( + const Region& owningRegion) : + owningRegion(owningRegion), + lineSize(owningRegion.maxCoords.x - owningRegion.minCoords.x + 1), + minZ(owningRegion.minCoords.z), + zDimSize(owningRegion.maxCoords.z - owningRegion.minCoords.z + 1), + maxY(owningRegion.maxCoords.y) { + + zDimCounter = 0; + + if (!owningRegion.isGuaranteedAllEmpty()) { + currentLocation = owningRegion.minCoords; + + pointerSkipAtEndOfXLine = InternalStorageType::voxelCoordOffsetToIndexOffset( + Vector3int16(0, 0, 1)); + pointerSkipAtEndOfZLine = InternalStorageType::voxelCoordOffsetToIndexOffset( + Vector3int16(0, 1, owningRegion.minCoords.z - owningRegion.maxCoords.z)); + + currentIndex = owningRegion.internalStorage->voxelCoordToArrayIndex(currentLocation); + currentCell = &owningRegion.internalStorage->getConstData()[currentIndex]; + reachedEnd = currentLocation.y > owningRegion.maxCoords.y; + + // index needs to be even for half byte material alignment reasons + RBXASSERT((currentIndex & 0x1) == 0); + } else { + reachedEnd = true; + } +} + +template +const Vector3int16& Region::xline_iterator::getCurrentLocation() const { + return currentLocation; +} + +template +unsigned int Region::xline_iterator::getLineSize() const { + return lineSize; +} + +template +const Cell* Region::xline_iterator::getLineCells() const { + return currentCell; +} + +template +const unsigned char* Region::xline_iterator::getLineMaterials() const { + return &owningRegion.internalStorage->getConstMaterial()[currentIndex / 2]; +} + +template +bool Region::xline_iterator::operator==( + const xline_iterator& other) const { + if (reachedEnd || other.reachedEnd) { + return reachedEnd == other.reachedEnd; + } + return owningRegion == other.owningRegion && + currentLocation == other.currentLocation; +} + +template +bool Region::xline_iterator::operator!=( + const xline_iterator& other) const { + return !(*this == other); +} + +template +typename Region::xline_iterator& +Region::xline_iterator::operator++() { + + ++currentLocation.z; + ++zDimCounter; + if (zDimCounter >= zDimSize) { + currentLocation.z = minZ; + zDimCounter = 0; + ++currentLocation.y; + currentIndex += pointerSkipAtEndOfZLine; + currentCell += pointerSkipAtEndOfZLine; + } else { + currentIndex += pointerSkipAtEndOfXLine; + currentCell += pointerSkipAtEndOfXLine; + } + + // index needs to be even for half byte material alignment reasons + RBXASSERT((currentIndex & 0x1) == 0); + + reachedEnd = currentLocation.y > maxY; + return *this; +} + + +} } diff --git a/App/voxel/Serializer.h b/App/voxel/Serializer.h new file mode 100644 index 0000000..dfa7acc --- /dev/null +++ b/App/voxel/Serializer.h @@ -0,0 +1,241 @@ +#pragma once + +#include "RBX/Debug.h" +#include "Util/ClusterCellIterator.h" +#include "Util/FixedSizeCircularBuffer.h" +#include "Util/G3DCore.h" +#include "Util/SpatialRegion.h" +#include "Util/VarInt.h" +#include "Voxel/Cell.h" +#include "Voxel/Grid.h" + +namespace RBX { namespace Voxel { + +class SerializerConstants { +public: + // these values are used for serializing cluster + // they are visible for testing + static const unsigned char kNewCellMarker; + static const unsigned char kRepeatCellMarker; + static const unsigned char kEndSequenceMarker; + static const unsigned int kRecentlyEncodedReferenceBits; +}; + +class Serializer { + typedef FixedSizeCircularBuffer RecentlyEncodedBuffer; + + template + void encodeFromPosition(const Grid* voxelStore, Vector3int16& cellpos, + const SpatialRegion::Id& lastChunkPos, const Grid::Region& region, + RecentlyEncodedBuffer& lastSeenNewCells, + CellBuffer& cellBuffer, OutputStream* outputStream) const { + + unsigned char cellValue = Cell::serializeAsUnsignedChar(region.voxelAt(cellpos)); + unsigned char materialValue = region.materialAt(cellpos); + + unsigned int content = (materialValue << 8) | cellValue; + Vector3int16 unread; + + unsigned int findIndex; + bool isOldContent = lastSeenNewCells.find(content, &findIndex); + + if (!isOldContent) { + outputStream->WriteBits(&SerializerConstants::kNewCellMarker, 2); + outputStream->WriteBits(&materialValue, 8); + outputStream->WriteBits(&cellValue, 8); + lastSeenNewCells.push(content); + CellBuffer::nextCellInIterationOrder(cellpos, &cellpos); + } else { + // TODO: The cell reads in this section aren't safe! They will read + // past the end of the cluster's data array. + unsigned int copyCount = 1; // this cell is a copy + Vector3int16 nextPos; + CellBuffer::nextCellInIterationOrder(cellpos, &nextPos); + + SpatialRegion::Id nextChunk = SpatialRegion::regionContainingVoxel(nextPos); + + unsigned char nextCellValue = Cell::serializeAsUnsignedChar(region.voxelAt(nextPos)); + unsigned char nextMaterialValue = region.materialAt(nextPos); + unsigned int nextContent = (nextMaterialValue << 8) | nextCellValue; + + while (nextChunk == lastChunkPos && cellBuffer.chk(nextPos) && + nextContent == content) { + copyCount++; + cellBuffer.pop(&unread); + RBXASSERT(nextPos == unread); + CellBuffer::nextCellInIterationOrder(nextPos, &nextPos); + nextChunk = SpatialRegion::regionContainingVoxel(nextPos); + nextCellValue = Cell::serializeAsUnsignedChar(region.voxelAt(nextPos)); + nextMaterialValue = region.materialAt(nextPos); + nextContent = (nextMaterialValue << 8) | nextCellValue; + } + + cellpos = nextPos; + outputStream->WriteBits(&SerializerConstants::kRepeatCellMarker, 2); + unsigned char charFindIndex = findIndex; + outputStream->WriteBits(&charFindIndex, SerializerConstants::kRecentlyEncodedReferenceBits); + VarInt<>::encode(*outputStream, copyCount); + } + } + +public: + + template + void encodeCells(const Grid* voxelStore, CellBuffer& cellBuffer, + OutputStream* outputStream, int sizeLimitInBytes) const { + + const Vector3int16 kCellInChunkBits( + SpatialRegion::getRegionDimensionInVoxelsAsBitShifts()); + + RecentlyEncodedBuffer lastSeenNewCells; + Grid::Region region; + SpatialRegion::Id lastChunkPos(SHRT_MIN, SHRT_MIN, SHRT_MIN); + while(cellBuffer.size() > 0 && (sizeLimitInBytes == -1 || ((int)outputStream->GetNumberOfBytesUsed()) < sizeLimitInBytes)) + { + Vector3int16 cellpos; + cellBuffer.pop(&cellpos); + + SpatialRegion::Id chunk = SpatialRegion::regionContainingVoxel(cellpos); + Vector3int16 cellModChunk = SpatialRegion::voxelCoordinateRelativeToEnclosingRegion(cellpos); + + unsigned char chunkChanged = 0; + if (chunk != lastChunkPos) { + lastChunkPos = chunk; + chunkChanged = 1; + outputStream->WriteBits(&chunkChanged, 1); + + // write a "0" to indicate that we aren't finished + chunkChanged = 0; + outputStream->WriteBits(&chunkChanged, 1); + + boost::int16_t data = chunk.value().x; + outputStream->WriteBits(reinterpret_cast(&data), 16); + data = chunk.value().y; + outputStream->WriteBits(reinterpret_cast(&data), 16); + data = chunk.value().z; + outputStream->WriteBits(reinterpret_cast(&data), 16); + + Region3int16 extents = SpatialRegion::inclusiveVoxelExtentsOfRegion(chunk); + region = voxelStore->getRegion(extents.getMinPos(), extents.getMaxPos()); + } else { + outputStream->WriteBits(&chunkChanged, 1); + } + + unsigned char data = cellModChunk.x; + outputStream->WriteBits(&data, kCellInChunkBits.x); + data = cellModChunk.y; + outputStream->WriteBits(&data, kCellInChunkBits.y); + data = cellModChunk.z; + outputStream->WriteBits(&data, kCellInChunkBits.z); + + Vector3int16& nextPos = cellpos; + bool continuing = true; + do { + encodeFromPosition(voxelStore, nextPos, lastChunkPos, region, lastSeenNewCells, + cellBuffer, outputStream); + continuing = cellBuffer.chk(nextPos) && + SpatialRegion::regionContainingVoxel(nextPos) == lastChunkPos && + (sizeLimitInBytes == -1 || ((int)outputStream->GetNumberOfBytesUsed()) < sizeLimitInBytes); + if (continuing) { + Vector3int16 unused; + cellBuffer.pop(&unused); + RBXASSERT(unused == nextPos); + } + } while (continuing); + + unsigned char endSequenceMarker = SerializerConstants::kEndSequenceMarker; + outputStream->WriteBits(&endSequenceMarker, 2); + } + + // write finalizer + unsigned char finalValue = 0xff; + // write 1 bit for chunk changed, and one bit to indicate EOM + outputStream->WriteBits(&finalValue, 2); + } + + template + void decodeCells(Grid* voxelStore, InputStream& inputStream, + CellUpdateFilter& filter) { + const Vector3int16 kCellInChunkBits( + SpatialRegion::getRegionDimensionInVoxelsAsBitShifts()); + + RecentlyEncodedBuffer lastSeenNewCells; + SpatialRegion::Id chunkPos(SHRT_MIN, SHRT_MIN, SHRT_MIN); + + while(1) + { + unsigned char changedChunk; + inputStream.ReadBits(&changedChunk, 1); + if (changedChunk) { + unsigned char eomTokenReceived = 0; + inputStream.ReadBits(&eomTokenReceived, 1); + if (eomTokenReceived) { + break; + } + + boost::int16_t x, y, z; + + inputStream.ReadBits(reinterpret_cast(&x), 16); + inputStream.ReadBits(reinterpret_cast(&y), 16); + inputStream.ReadBits(reinterpret_cast(&z), 16); + + chunkPos = SpatialRegion::Id(x, y, z); + } + Vector3int16 cellPos(0,0,0); + + unsigned char data; + inputStream.ReadBits(&data, kCellInChunkBits.x); + cellPos.x = data; + inputStream.ReadBits(&data, kCellInChunkBits.y); + cellPos.y = data; + inputStream.ReadBits(&data, kCellInChunkBits.z); + cellPos.z = data; + cellPos = SpatialRegion::globalVoxelCoordinateFromRegionAndRelativeCoordinate( + chunkPos, cellPos); + unsigned char controlBits; + do { + inputStream.ReadBits(&controlBits, 2); + if (controlBits == SerializerConstants::kNewCellMarker) { + unsigned char material, cell; + inputStream.ReadBits(&material, 8); + inputStream.ReadBits(&cell, 8); + + unsigned int content = (material << 8) | cell; + + lastSeenNewCells.push(content); + + if (filter.canSet(cellPos)) { + voxelStore->setCell(cellPos, Cell::deserializeFromUnsignedChar(cell), (CellMaterial)material); + } + // advance cellPos + CellBuffer::nextCellInIterationOrder(cellPos, &cellPos); + } else if (controlBits == SerializerConstants::kRepeatCellMarker) { + unsigned char backIndex; + inputStream.ReadBits(&backIndex, SerializerConstants::kRecentlyEncodedReferenceBits); + unsigned int count = 0; + VarInt<>::decode(inputStream, &count); + RBXASSERT(count > 0); + + unsigned int content = lastSeenNewCells[backIndex]; + unsigned char material = content >> 8; + unsigned char cell = content & 0xFF; + + do { + if (filter.canSet(cellPos)) { + voxelStore->setCell(cellPos, + Cell::deserializeFromUnsignedChar(cell), + (CellMaterial)material); + } + CellBuffer::nextCellInIterationOrder(cellPos, &cellPos); + count--; + } while (count); + // at this point cellPos points to the next cell after the sequence + } + } while(controlBits != SerializerConstants::kEndSequenceMarker); + // do not read cellPos after this line, the do {} while() loop ends with + // cellPos at an invalid cell. + } + } +}; + +} } // namespace RBX diff --git a/App/voxel/Util.h b/App/voxel/Util.h new file mode 100644 index 0000000..7657e50 --- /dev/null +++ b/App/voxel/Util.h @@ -0,0 +1,167 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "Voxel/Cell.h" +#include "rbx/Debug.h" +#include "Util/Extents.h" +#include "Util/Region3int16.h" + +//////////////////////////////////////////////////////////////////////////////// +// This file has methods for reading and writing individual voxel cells + +namespace RBX { namespace Voxel { + +inline CellMaterial getCellMaterial_Deprecated( unsigned char cell ) { return (CellMaterial)(cell & 0x07); } +inline void setCellMaterial_Deprecated( unsigned char& cell, CellMaterial material ) { cell = (cell & 0xf8) | ((int)material & 0x07); } + +inline CellMaterial readMaterial(const unsigned char* materials, const unsigned int cellIndex, const Cell cell) { + return (CellMaterial)( + cell.solid.getBlock() == CELL_BLOCK_Empty ? + CELL_MATERIAL_Water : + ((materials[cellIndex >> 1] >> (4 * (cellIndex & 0x1))) & 0x0f) + 1); +} +inline void writeMaterial(unsigned char* materials, unsigned int cellIndex, const CellMaterial newMaterial) { + RBXASSERT(newMaterial > 0); + unsigned char& wholeByte = materials[cellIndex >> 1]; + unsigned int shift = (4 * (cellIndex & 0x1)); + unsigned char mask = 0x0f << shift; + wholeByte &= (~mask); + wholeByte |= (((newMaterial-1) << shift) & mask); +} + +enum FaceDirection +{ + PlusX = 0, + PlusZ = 1, + MinusX = 2, + MinusZ = 3, + PlusY = 4, + MinusY = 5, + Invalid = 6 +}; + +struct BlockAxisFace { + enum SkippedCorner { + TopRight = 0, + TopLeft = 1, + BottomLeft = 2, + BottomRight = 3, + EmptyAllSkipped = 4, + FullNoneSkipped = 5 + }; + + SkippedCorner skippedCorner; + + static inline SkippedCorner rotate(SkippedCorner corner, const CellOrientation orient) { + return (SkippedCorner) (corner < 4 ? (corner + orient) % 4 : corner); + } + + static inline bool divideTopLeftToBottomRight(SkippedCorner corner) { + return corner == TopRight || corner == BottomLeft || corner == FullNoneSkipped; + } + + static inline SkippedCorner XZAxisMirror(SkippedCorner corner) { + static SkippedCorner MIRROR[6] = + { TopLeft, TopRight, BottomRight, BottomLeft, EmptyAllSkipped, FullNoneSkipped }; + return MIRROR[corner]; + } + + static inline SkippedCorner YAxisMirror(SkippedCorner corner) { + static SkippedCorner MIRROR[6] = + { BottomRight, BottomLeft, TopLeft, TopRight, EmptyAllSkipped, FullNoneSkipped }; + return MIRROR[corner]; + } + + static BlockAxisFace inverse(const BlockAxisFace other) { + static const SkippedCorner OPPOSITE_CORNER[6] = { + BottomLeft, + BottomRight, + TopRight, + TopLeft, + FullNoneSkipped, + EmptyAllSkipped + }; + + BlockAxisFace out; + out.skippedCorner = OPPOSITE_CORNER[other.skippedCorner]; + return out; + } +}; + +struct BlockFaceInfo { + // indexed by FaceDirection + BlockAxisFace faces[6]; +}; + +extern const BlockFaceInfo UnOrientedBlockFaceInfos[6]; +extern BlockAxisFace OrientedFaceMap[ 1536 ]; // 2^8 * 6 + +// ComputeOrientedFace is not declared because it is an implementation detail +void initBlockOrientationFaceMap(); + +inline const BlockAxisFace& GetOrientedFace(Cell cell, FaceDirection f) +{ + return OrientedFaceMap[ Cell::asUnsignedCharForDeprecatedUses(cell)*6 + f ]; +} + +inline bool isWedgeSideNotFull(Cell voxel, FaceDirection f) { + return GetOrientedFace(voxel, f).skippedCorner != BlockAxisFace::FullNoneSkipped; +} + +inline Vector3int16 worldToCell_floor(const Vector3& worldPos) { + const int kXZOffset = 0; + return Vector3int16( + (int)(floorf(worldPos.x / kCELL_SIZE)) + kXZOffset, + (int)(floorf(worldPos.y / kCELL_SIZE)), + (int)(floorf(worldPos.z / kCELL_SIZE)) + kXZOffset); +} + +inline Vector3 worldSpaceToCellSpace(const Vector3& worldPos) { + return Vector3( + (worldPos.x * (1.0f / kCELL_SIZE)), + (worldPos.y * (1.0f / kCELL_SIZE)), + (worldPos.z * (1.0f / kCELL_SIZE))); +} + +inline Vector3 cellSpaceToWorldSpace(const Vector3& cellPos) +{ + return Vector3( + (cellPos.x * kCELL_SIZE), + (cellPos.y * kCELL_SIZE), + (cellPos.z * kCELL_SIZE)); +} + + +inline Vector3 cellToWorld_smallestCorner(const Vector3int16& cellPos) { + const int kXZOffset = 0; + return Vector3( + (cellPos.x - kXZOffset) * kCELL_SIZE, + cellPos.y * kCELL_SIZE, + (cellPos.z - kXZOffset) * kCELL_SIZE); + +} + +inline Vector3 cellToWorld_center(const Vector3int16& cellPos) { + Vector3 pos = cellToWorld_smallestCorner(cellPos); + return pos + Vector3(kHALF_CELL, kHALF_CELL, kHALF_CELL); +} + +inline Vector3 cellToWorld_largestCorner(const Vector3int16& cellPos) { + return cellToWorld_smallestCorner(cellPos + Vector3int16(1, 1, 1)); +} + +inline Region3int16 getTerrainExtentsInCells() +{ + const int kRadius = 32000; + + return Region3int16(Vector3int16(-kRadius, -kRadius, -kRadius), Vector3int16(kRadius, kRadius, kRadius)); +} + +inline Extents getTerrainExtents() +{ + Region3int16 extents = getTerrainExtentsInCells(); + + return Extents(cellToWorld_smallestCorner(extents.getMinPos()), cellToWorld_largestCorner(extents.getMaxPos())); +} + +} } diff --git a/App/voxel/Voxelizer.h b/App/voxel/Voxelizer.h new file mode 100644 index 0000000..cd52bdf --- /dev/null +++ b/App/voxel/Voxelizer.h @@ -0,0 +1,104 @@ +#pragma once +// suffix header file for Grid.h + +#include "Util/SpatialRegion.h" +#include "Util/Extents.h" + +#include + +namespace RBX { + class MegaClusterInstance; + class ContactManager; + class PartInstance; + + namespace Voxel { class Grid; } + namespace Voxel2 { class Grid; } + + const int kVoxelChunkSizeXZ = 32; + const int kVoxelChunkSizeY = 16; + + const Vector3int32 kVoxelChunkSize = Vector3int32(kVoxelChunkSizeXZ, kVoxelChunkSizeY, kVoxelChunkSizeXZ); + +namespace Voxel { + +struct OccupancyChunk +{ + unsigned int dirty; + unsigned int age; + Vector3int32 index; + unsigned char occupancy[kVoxelChunkSizeY][kVoxelChunkSizeXZ][kVoxelChunkSizeXZ]; + Extents getChunkExtents() const; +}; + +struct DataModelPartCache; + +class Voxelizer +{ +public: + Voxelizer(bool collisionTransparency = false); + + void occupancyUpdateChunk(OccupancyChunk& chunk, MegaClusterInstance* terrain, ContactManager* contactManager); + + void occupancyUpdateChunkPrepare(OccupancyChunk& chunk, MegaClusterInstance* terrain, ContactManager* contactManager, std::vector& partCache); + void occupancyUpdateChunkPerform(const std::vector& partCache); + + void setNonFixedPartsEnabled(bool value) { nonFixedPartsEnabled = value; } + bool getNonFixedPartsEnabled() const { return nonFixedPartsEnabled; } + +private: + void occupancyFillTerrainMega(OccupancyChunk& chunk, Voxel::Grid& terrain, const Vector3int32& chunkOffset, const Extents& chunkExtents); + void occupancyFillTerrainMegaSIMD(OccupancyChunk& chunk, Voxel::Grid& terrain, const Vector3int32& chunkOffset, const Extents& chunkExtents); + + void occupancyFillTerrainSmooth(OccupancyChunk& chunk, Voxel2::Grid& terrain, const Extents& chunkExtents); + void occupancyFillTerrainSmoothSIMD(OccupancyChunk& chunk, Voxel2::Grid& terrain, const Extents& chunkExtents); + + void occupancyFillBlock(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency, float meshRadius); + void occupancyFillBlockDF(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency); + void occupancyFillBlockDFAA(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency); + void occupancyFillBlockDFSIMD(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency); + + void occupancyFillSphere(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency, float meshRadius); + void occupancyFillEllipsoid(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency, float meshRadius); + void occupancyFillCylinderX(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency, float meshRadius); + void occupancyFillCylinderY(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency, float meshRadius); + void occupancyFillWedge(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency, float meshRadius); + void occupancyFillCornerWedge(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency, float meshRadius); + void occupancyFillTorso(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency, float meshRadius); + void occupancyFillMesh(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency, float meshRadius); + + void addMeshToPartCache(std::vector& partCache, PartInstance* part, OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents_, const CoordinateFrame& cframe, float transparency); + + template void occupancyFillDF(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency, DistanceFunction& df); + + float getEffectiveTransparency(PartInstance* part); + + bool useSIMD; + bool nonFixedPartsEnabled; + bool collisionTransparency; +}; + +struct DataModelPartCache +{ + typedef void (Voxelizer::*pfn)(OccupancyChunk& chunk, const Extents& chunkExtents, const Vector3& extents, const CoordinateFrame& cframe, float transparency, float meshRadius); + + pfn fillFunc; + OccupancyChunk* chunk; + Vector3 extents; + CoordinateFrame cframe; + float transparency; + float meshRadius; + + DataModelPartCache(pfn fillFunc, OccupancyChunk& chunk, const Vector3& extents, const CoordinateFrame& cframe, float transparency, float meshRadius = 0) + : fillFunc(fillFunc) + , chunk(&chunk) + , extents(extents) + , cframe(cframe) + , transparency(transparency) + , meshRadius(meshRadius) + { + } +}; + + +} } + diff --git a/App/voxel/Water.h b/App/voxel/Water.h new file mode 100644 index 0000000..c9de434 --- /dev/null +++ b/App/voxel/Water.h @@ -0,0 +1,40 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "Voxel/Util.h" + +namespace RBX { namespace Voxel { + +namespace Water { + // Generate relative cell coords relevant to the water on wedge state of a + // cell. Some locations will be initialized to the center location if they + // are irelevant to the water on wedge state. + struct RelevantNeighbors { + const Vector3int16 aboveNeighbor; + const Vector3int16 primaryNeighbor; + const Vector3int16 secondaryNeighbor; + const Vector3int16 diagonalNeighbor; + const Vector3int16 diagonalUpNeighbor; + + RelevantNeighbors(CellOrientation orientation); + }; + + struct LocalAreaInfo { + Cell aboveNeighbor; + Cell primaryNeighbor; + Cell secondaryNeighbor; + Cell diagonalNeighbor; + Cell diagonalUpNeighbor; + }; + + template + inline bool cellHasWater(const BoxType* reader, const Cell& cell, + const Vector3int16& globalCoord); + template + Cell interpretAsWaterCell(const BoxType* reader, const Cell& cell, + const Vector3int16& globalCoord); +} + +} } + +#include "Voxel/Water.inl" diff --git a/App/voxel/Water.inl b/App/voxel/Water.inl new file mode 100644 index 0000000..d044561 --- /dev/null +++ b/App/voxel/Water.inl @@ -0,0 +1,137 @@ +#pragma once + +#include "Voxel/Util.h" + +namespace RBX { namespace Voxel { + +namespace Water { + +extern const RelevantNeighbors kRelevantNeighbors[MAX_CELL_ORIENTATIONS]; + +namespace { + +const FaceDirection kOppositeFaceDirection[Invalid] = { + MinusX, + MinusZ, + PlusX, + PlusZ, + MinusY, + PlusY, +}; + +const FaceDirection kPrimaryNeighborByOrientation[MAX_CELL_ORIENTATIONS] = { + PlusZ, + PlusX, + MinusZ, + MinusX +}; + +const FaceDirection kSecondaryNeighborByOrientation[MAX_CELL_ORIENTATIONS] = { + MinusX, + PlusZ, + PlusX, + MinusZ +}; + +const Vector3int16 kAboveNeighborCellOffset(0,1,0); +const Vector3int16 kPrimaryNeighborCellOffset[MAX_CELL_ORIENTATIONS] = { + Vector3int16(0,0,1), + Vector3int16(1,0,0), + Vector3int16(0,0,-1), + Vector3int16(-1,0,0), +}; +const Vector3int16 kSecondaryNeighborCellOffset[MAX_CELL_ORIENTATIONS] = { + Vector3int16(-1,0,0), + Vector3int16(0,0,1), + Vector3int16(1,0,0), + Vector3int16(0,0,-1), +}; + +bool isWaterOnWedge(const Cell& center, const LocalAreaInfo& info) { + if (center.solid.getBlock() != CELL_BLOCK_Empty && center.solid.getBlock() != CELL_BLOCK_Solid) { + + if (info.aboveNeighbor.isExplicitWaterCell()) { + return true; + } + + CellOrientation cellOrientation = center.solid.getOrientation(); + FaceDirection primaryDirection = kPrimaryNeighborByOrientation[cellOrientation]; + const Cell& primaryNeighbor = info.primaryNeighbor; + + if (center.solid.getBlock() == CELL_BLOCK_VerticalWedge) { + return primaryNeighbor.isExplicitWaterCell(); + } else { + bool isPrimaryNeighborWater = primaryNeighbor.isExplicitWaterCell(); + bool isPrimarysSharedFaceNotSolidAndNotEmpty = !primaryNeighbor.isEmpty() && + isWedgeSideNotFull(primaryNeighbor, kOppositeFaceDirection[primaryDirection]); + + FaceDirection secondaryDirection = kSecondaryNeighborByOrientation[cellOrientation]; + const Cell& secondaryNeighbor = info.secondaryNeighbor; + bool isSecondaryNeighborWater = secondaryNeighbor.isExplicitWaterCell(); + bool isSecondarysSharedFaceNotSolidAndNotEmpty = !secondaryNeighbor.isEmpty() && + isWedgeSideNotFull(secondaryNeighbor, kOppositeFaceDirection[secondaryDirection]); + + const Cell& diagonalNeighbor = info.diagonalNeighbor; + bool isDiagonalWater = diagonalNeighbor.isExplicitWaterCell(); + + // add a special case for inv corner water wedges: + // * can check the x, z, and +y offsets + // * the block is an InverseCornerWedge + // * x, z, and x+z offsets are all seperately not empty + // * x + z + y offsets taken together contains explicit water + bool inverseCornerWedgeVerticalDiagonalWaterCase = + center.solid.getBlock() == CELL_BLOCK_InverseCornerWedge && + !primaryNeighbor.isEmpty() && + !secondaryNeighbor.isEmpty() && + !diagonalNeighbor.isEmpty() && + info.diagonalUpNeighbor.isExplicitWaterCell(); + + bool bothOrthoNeighborsAreExplicitWater = isPrimaryNeighborWater && isSecondaryNeighborWater; + + bool bothOrthoNeighborsSupportDiagonalWater = + (isPrimaryNeighborWater || isPrimarysSharedFaceNotSolidAndNotEmpty) && + (isSecondaryNeighborWater || isSecondarysSharedFaceNotSolidAndNotEmpty); + + return + inverseCornerWedgeVerticalDiagonalWaterCase || + (isDiagonalWater && bothOrthoNeighborsSupportDiagonalWater) || + bothOrthoNeighborsAreExplicitWater; + } + } + return false; +} + +template +bool isWaterOnWedge(const BoxType* reader, const Cell& cell, const Vector3int16& globalCoord) { + LocalAreaInfo info; + reader->fillLocalAreaInfo(globalCoord, kRelevantNeighbors[cell.solid.getOrientation()], &info); + return isWaterOnWedge(cell, info); +} + +} + +template +bool cellHasWater(const BoxType* reader, const Cell& center, + const Vector3int16& globalCoord) { + return !center.isEmpty() && + center.solid.getBlock() != CELL_BLOCK_Solid && + (center.solid.getBlock() == CELL_BLOCK_Empty || isWaterOnWedge(reader, center, globalCoord)); +} + +template +Cell interpretAsWaterCell(const BoxType* reader, const Cell& cell, + const Vector3int16& globalCoord) { + if (cellHasWater(reader, cell, globalCoord)) { + if (cell.solid.getBlock() == CELL_BLOCK_Empty) { + return cell; + } else { + return Constants::kWaterOnWedgeCell; + } + } else { + return Constants::kUniqueEmptyCellRepresentation; + } +} + +} // Water +} } + diff --git a/App/voxel2/BitSerializer.h b/App/voxel2/BitSerializer.h new file mode 100644 index 0000000..331ed18 --- /dev/null +++ b/App/voxel2/BitSerializer.h @@ -0,0 +1,275 @@ +#pragma once + +#include "voxel2/Grid.h" + +namespace RBX { namespace Voxel2 { + +template class BitSerializer +{ +public: + void encodeIndex(const Vector3int32& index, BitStream& stream) + { + encodeChunkIndex(index - lastIndex, stream); + + lastIndex = index; + } + + void encodeContent(const Box& box, BitStream& stream) + { + encodeChunkData(box, stream); + } + + void decodeIndex(Vector3int32& index, BitStream& stream) + { + Vector3int32 diff; + decodeChunkIndex(diff, stream); + + index = lastIndex + diff; + lastIndex = index; + } + + void decodeContent(Box& box, BitStream& stream) + { + decodeChunkData(box, stream); + } + +private: + Vector3int32 lastIndex; + std::vector cells; + + void encodeChunkIndex(const Vector3int32& diff, BitStream& stream) + { + if (char(diff.x) == diff.x && char(diff.y) == diff.y && char(diff.z) == diff.z) + { + // Single-byte diffs: tag "1" + stream << true; + stream << char(diff.x); + stream << char(diff.y); + stream << char(diff.z); + } + else if (short(diff.x) == diff.x && short(diff.y) == diff.y && short(diff.z) == diff.z) + { + // Two-byte diffs: tag "01" + stream << false; + stream << true; + stream << short(diff.x); + stream << short(diff.y); + stream << short(diff.z); + } + else + { + // Four-byte diffs: tag "00" + stream << false; + stream << false; + stream << diff.x; + stream << diff.y; + stream << diff.z; + } + } + + void decodeChunkIndex(Vector3int32& diff, BitStream& stream) + { + bool size1; + stream >> size1; + + if (size1) + { + // Single-byte diffs: tag "1" + char x, y, z; + stream >> x; + stream >> y; + stream >> z; + + diff = Vector3int32(x, y, z); + } + else + { + bool size2; + stream >> size2; + + if (size2) + { + // Two-byte diffs: tag "01" + short x, y, z; + stream >> x; + stream >> y; + stream >> z; + + diff = Vector3int32(x, y, z); + } + else + { + // Four-byte diffs: tag "00" + int x, y, z; + stream >> x; + stream >> y; + stream >> z; + + diff = Vector3int32(x, y, z); + } + } + } + + void encodeChunkData(const Box& box, BitStream& stream) + { + bool empty = box.isEmpty(); + + stream << empty; + + if (empty) + return; + + Vector3int32 size = box.getSize(); + + cells.resize(size.x * size.y * size.z); + + unsigned int cellOffset = 0; + + for (int y = 0; y < size.y; ++y) + for (int z = 0; z < size.z; ++z) + { + memcpy(&cells[cellOffset], box.readRow(0, y, z), size.x * sizeof(Cell)); + cellOffset += size.x; + } + + int lastMaterial = 0; + + for (unsigned int offset = 0; offset < cells.size(); ) + { + // identify run length + Cell cell = cells[offset]; + unsigned int count = 0; + + do offset++, count++; + while (offset < cells.size() && cells[offset] == cell && count < 512); + + // serialize run length + // 00 = single cell + // xx = x groups of 3-bit values (max is 3 groups of 3-bit values = 9 bit = 512) + unsigned char groups = (count == 1) ? 0 : (count <= 8) ? 1 : (count <= 64) ? 2 : 3; + unsigned int temp = count - 1; + + stream.WriteBits(&groups, 2); + stream.WriteBits((const unsigned char*)&temp, groups * 3); + + // serialize material/occupancy combo + // 0 = air + // 10 = full (occupancy is assumed to be max) + // 11 = custom (followed by 8 bits with occupancy data) + + // material (only if it's not air) + // 0 = last + // 1 = new (followed by 6 bits with material data) + if (cell.getMaterial() == Cell::Material_Air) + stream << false; + else + { + // solid + stream << true; + + // customOccupancy + if (cell.getOccupancy() == Cell::Occupancy_Max) + stream << false; + else + { + stream << true; + + unsigned char occupancy = cell.getOccupancy(); + stream.WriteBits(&occupancy, Cell::Occupancy_Bits); + } + + // newMaterial + if (cell.getMaterial() == lastMaterial) + stream << false; + else + { + stream << true; + + unsigned char material = cell.getMaterial(); + stream.WriteBits(&material, Cell::Material_Bits); + } + + lastMaterial = cell.getMaterial(); + } + } + } + + void decodeChunkData(Box& box, BitStream& stream) + { + RBXASSERT(box.isEmpty()); + + bool empty; + stream >> empty; + + if (empty) + return; + + Vector3int32 size = box.getSize(); + + cells.resize(size.x * size.y * size.z); + + int lastMaterial = 0; + + for (unsigned int offset = 0; offset < cells.size(); ) + { + // deserialize run length + unsigned char groups = 0; + stream.ReadBits(&groups, 2); + + unsigned int temp = 0; + stream.ReadBits((unsigned char*)&temp, groups * 3); + + unsigned int count = temp + 1; + + if (offset + count > cells.size()) + throw RBX::runtime_error("Error while decoding data: chunk overflow at %u cells", offset + count); + + // deserialize material/occupancy + unsigned char material = Cell::Material_Air; + unsigned char occupancy = 0; + + bool solid; + stream >> solid; + + if (solid) + { + bool customOccupancy; + stream >> customOccupancy; + + if (!customOccupancy) + occupancy = Cell::Occupancy_Max; + else + stream.ReadBits(&occupancy, Cell::Occupancy_Bits); + + bool newMaterial; + stream >> newMaterial; + + if (!newMaterial) + material = lastMaterial; + else + stream.ReadBits(&material, Cell::Material_Bits); + + lastMaterial = material; + } + + // fill cells + Cell cell(material, occupancy); + + for (unsigned int i = 0; i < count; ++i) + cells[offset + i] = cell; + + offset += count; + } + + unsigned int cellOffset = 0; + + for (int y = 0; y < size.y; ++y) + for (int z = 0; z < size.z; ++z) + { + memcpy(box.writeRow(0, y, z), &cells[cellOffset], size.x * sizeof(Cell)); + cellOffset += size.x; + } + } +}; + +} } diff --git a/App/voxel2/Conversion.h b/App/voxel2/Conversion.h new file mode 100644 index 0000000..37dee10 --- /dev/null +++ b/App/voxel2/Conversion.h @@ -0,0 +1,192 @@ +#pragma once + +#include "v8datamodel/PartInstance.h" + +#include "voxel/Grid.h" +#include "voxel2/Grid.h" + +namespace RBX { namespace Voxel2 { namespace Conversion { + + static const int kOccupancySolid = Cell::Occupancy_Max; + static const int kOccupancyWedge = Cell::Occupancy_Max / 2; + static const int kOccupancyCorner = Cell::Occupancy_Max / 3; + static const int kOccupancyInverseCorner = Cell::Occupancy_Max * 2 / 3; + + static const PartMaterial kMaterialTable[] = + { + AIR_MATERIAL, + WATER_MATERIAL, + GRASS_MATERIAL, + SLATE_MATERIAL, + CONCRETE_MATERIAL, + BRICK_MATERIAL, + SAND_MATERIAL, + WOODPLANKS_MATERIAL, + ROCK_MATERIAL, + GLACIER_MATERIAL, + SNOW_MATERIAL, + SANDSTONE_MATERIAL, + MUD_MATERIAL, + BASALT_MATERIAL, + GROUND_MATERIAL, + CRACKED_LAVA_MATERIAL, + }; + + static const int kMaterialDefault = 2; + + inline unsigned char getOccupancyFromSolidBlock(Voxel::CellBlock block) + { + switch (block) + { + case Voxel::CELL_BLOCK_Solid: + return kOccupancySolid; + + case Voxel::CELL_BLOCK_VerticalWedge: + case Voxel::CELL_BLOCK_HorizontalWedge: + return kOccupancyWedge; + + case Voxel::CELL_BLOCK_CornerWedge: + return kOccupancyCorner; + + case Voxel::CELL_BLOCK_InverseCornerWedge: + return kOccupancyInverseCorner; + + default: + RBXASSERT(false); + return 0; + } + } + + inline Voxel::CellBlock getCellBlockFromCell(const Cell& cell) + { + static const int kOccupancyRounder = Cell::Occupancy_Max / 6; + + if (cell.getMaterial() == Cell::Material_Air) + return Voxel::CELL_BLOCK_Empty; + else if (cell.getOccupancy() < kOccupancyCorner - kOccupancyRounder) + return Voxel::CELL_BLOCK_Empty; + else if (cell.getOccupancy() < kOccupancyWedge - kOccupancyRounder) + return Voxel::CELL_BLOCK_CornerWedge; + else if (cell.getOccupancy() < kOccupancyInverseCorner - kOccupancyRounder) + return Voxel::CELL_BLOCK_VerticalWedge; + else if (cell.getOccupancy() < kOccupancySolid - kOccupancyRounder) + return Voxel::CELL_BLOCK_InverseCornerWedge; + else + return Voxel::CELL_BLOCK_Solid; + } + + inline PartMaterial getMaterialFromVoxelMaterial(unsigned char material) + { + if (static_cast(material) < sizeof(kMaterialTable) / sizeof(kMaterialTable[0])) + return kMaterialTable[material]; + else + return kMaterialTable[kMaterialDefault]; + } + + inline unsigned char getVoxelMaterialFromMaterial(PartMaterial material) + { + for (size_t i = 0; i < sizeof(kMaterialTable) / sizeof(kMaterialTable[0]); ++i) + if (kMaterialTable[i] == material) + return i; + + return kMaterialDefault; + } + + inline PartMaterial getMaterialFromCellMaterial(Voxel::CellMaterial material) + { + switch (material) + { + case Voxel::CELL_MATERIAL_Deprecated_Empty: + return AIR_MATERIAL; + case Voxel::CELL_MATERIAL_Grass: + return GRASS_MATERIAL; + case Voxel::CELL_MATERIAL_Sand: + return SAND_MATERIAL; + case Voxel::CELL_MATERIAL_Brick: + return BRICK_MATERIAL; + case Voxel::CELL_MATERIAL_Granite: + return SLATE_MATERIAL; + case Voxel::CELL_MATERIAL_Asphalt: + return CONCRETE_MATERIAL; + case Voxel::CELL_MATERIAL_Wood_Plank: + case Voxel::CELL_MATERIAL_Wood_Log: + return WOODPLANKS_MATERIAL; + case Voxel::CELL_MATERIAL_Gravel: + return SLATE_MATERIAL; + case Voxel::CELL_MATERIAL_Cinder_Block: + return CONCRETE_MATERIAL; + case Voxel::CELL_MATERIAL_Stone_Block: + return SLATE_MATERIAL; + case Voxel::CELL_MATERIAL_Cement: + return CONCRETE_MATERIAL; + case Voxel::CELL_MATERIAL_Water: + return WATER_MATERIAL; + default: + return kMaterialTable[kMaterialDefault]; + } + } + + inline Voxel::CellMaterial getCellMaterialFromMaterial(PartMaterial material) + { + switch (material) + { + case AIR_MATERIAL: + return Voxel::CELL_MATERIAL_Deprecated_Empty; + case WATER_MATERIAL: + return Voxel::CELL_MATERIAL_Water; + case GRASS_MATERIAL: + return Voxel::CELL_MATERIAL_Grass; + case SLATE_MATERIAL: + return Voxel::CELL_MATERIAL_Stone_Block; + case CONCRETE_MATERIAL: + return Voxel::CELL_MATERIAL_Cement; + case BRICK_MATERIAL: + return Voxel::CELL_MATERIAL_Brick; + case SAND_MATERIAL: + return Voxel::CELL_MATERIAL_Sand; + case WOODPLANKS_MATERIAL: + return Voxel::CELL_MATERIAL_Wood_Plank; + default: + return Voxel::CELL_MATERIAL_Grass; + } + } + + inline void convertToSmooth(const Voxel::Grid& oldGrid, Voxel2::Grid& grid) + { + std::vector chunks = oldGrid.getNonEmptyChunks(); + + for (size_t i = 0; i < chunks.size(); ++i) + { + Region3int16 extents = SpatialRegion::inclusiveVoxelExtentsOfRegion(chunks[i]); + Voxel::Grid::Region region = oldGrid.getRegion(extents.getMinPos(), extents.getMaxPos()); + + Voxel2::Box box(Voxel::kXZ_CHUNK_SIZE, Voxel::kY_CHUNK_SIZE, Voxel::kXZ_CHUNK_SIZE); + + for (int y = 0; y < Voxel::kY_CHUNK_SIZE; ++y) + for (int z = 0; z < Voxel::kXZ_CHUNK_SIZE; ++z) + for (int x = 0; x < Voxel::kXZ_CHUNK_SIZE; ++x) + { + Vector3int16 cpos = extents.getMinPos() + Vector3int16(x, y, z); + const Voxel::Cell& oldCell = region.voxelAt(cpos); + const Voxel::CellMaterial& oldMaterial = region.materialAt(cpos); + + if (!oldCell.isEmpty()) + { + using namespace Voxel2::Conversion; + + Voxel2::Cell cell; + + if (oldCell.isExplicitWaterCell()) + cell = Voxel2::Cell(Voxel2::Cell::Material_Water, Voxel2::Cell::Occupancy_Max); + else + cell = Voxel2::Cell(getVoxelMaterialFromMaterial(getMaterialFromCellMaterial(oldMaterial)), getOccupancyFromSolidBlock(oldCell.solid.getBlock())); + + box.set(x, y, z, cell); + } + } + + grid.write(Voxel2::Region(Vector3int32(extents.getMinPos()), Vector3int32(extents.getMaxPos() + Vector3int16(1, 1, 1))), box); + } + } + +} } } diff --git a/App/voxel2/Grid.h b/App/voxel2/Grid.h new file mode 100644 index 0000000..f2caa84 --- /dev/null +++ b/App/voxel2/Grid.h @@ -0,0 +1,205 @@ +#pragma once + +#include "util/Vector3int32.h" +#include + +namespace RBX { namespace Voxel2 { + + class Cell + { + public: + enum Material + { + Material_Air = 0, + Material_Water = 1, + + Material_Bits = 6, + Material_Max = (1 << Material_Bits) - 1 + }; + + enum Occupancy + { + Occupancy_Bits = 8, + Occupancy_Max = (1 << Occupancy_Bits) - 1 + }; + + Cell() + { + // we rely on Air being 0 since we use memset elsewhere + BOOST_STATIC_ASSERT(Material_Air == 0); + + this->material = 0; + this->occupancy = 0; + } + + Cell(unsigned char material, unsigned char occupancy) + { + RBXASSERT_VERY_FAST(material <= Material_Max && occupancy <= Occupancy_Max); + + // make sure occupancy is always 0 for Air material (0) + this->material = material; + this->occupancy = occupancy & (-static_cast(material) >> 31); + } + + unsigned char getMaterial() const { return material; } + unsigned char getOccupancy() const { return occupancy; } + + bool operator==(const Cell& other) const { return material == other.material && occupancy == other.occupancy; } + bool operator!=(const Cell& other) const { return !(*this == other); } + + private: + unsigned char material; + unsigned char occupancy; + }; + + class Region + { + public: + Region(const Vector3int32& begin, const Vector3int32& end) + : begin_(begin) + , end_(end) + { + RBXASSERT_VERY_FAST(begin.x <= end.x && begin.y <= end.y && begin.z <= end.z); + } + + Region(const Vector3int32& begin, unsigned int size) + : begin_(begin) + , end_(begin + Vector3int32(size, size, size)) + { + } + + static Region fromChunk(const Vector3int32& id, unsigned int chunkSizeLog2) + { + return Region(id << chunkSizeLog2, 1 << chunkSizeLog2); + } + + static Region fromExtents(const Vector3& min, const Vector3& max); + + const Vector3int32& begin() const { return begin_; } + const Vector3int32& end() const { return end_; } + + Vector3int32 size() const { return end_ - begin_; } + + bool empty() const { return begin_.x == end_.x || begin_.y == end_.y || begin_.z == end_.z; } + + bool operator==(const Region& other) const { return begin_ == other.begin_ && end_ == other.end_; } + bool operator!=(const Region& other) const { return begin_ != other.begin_ || end_ != other.end_; } + + bool aligned(unsigned int size) const; + bool inside(const Region& other) const; + + Region intersect(const Region& other) const; + Region expand(unsigned int size) const; + Region expandToGrid(unsigned int size) const; + Region offset(const Vector3int32& offset) const; + Region downsample(unsigned int lod) const; + + std::vector getChunkIds(unsigned int chunkSizeLog2) const; + unsigned long long getChunkCount(unsigned int chunkSizeLog2) const; + + private: + Vector3int32 begin_; + Vector3int32 end_; + }; + + class Box + { + public: + Box(); + Box(int sizeX, int sizeY, int sizeZ); + + const Cell& get(int x, int y, int z) const + { + RBXASSERT_VERY_FAST(static_cast(x) < static_cast(sizeX) && static_cast(y) < static_cast(sizeY) && static_cast(z) < static_cast(sizeZ)); + return data.get() ? data[x + sizeX * z + sliceXZ * y] : emptyCell; + } + + void set(int x, int y, int z, const Cell& cell) + { + RBXASSERT_VERY_FAST(static_cast(x) < static_cast(sizeX) && static_cast(y) < static_cast(sizeY) && static_cast(z) < static_cast(sizeZ)); + if (!data) allocate(); + data[x + sizeX * z + sliceXZ * y] = cell; + } + + const Cell* readRow(int x, int y, int z) const + { + RBXASSERT_VERY_FAST(static_cast(x) < static_cast(sizeX) && static_cast(y) < static_cast(sizeY) && static_cast(z) < static_cast(sizeZ)); + RBXASSERT_VERY_FAST(data.get()); + return &data[x + sizeX * z + sliceXZ * y]; + } + + Cell* writeRow(int x, int y, int z) + { + RBXASSERT_VERY_FAST(static_cast(x) < static_cast(sizeX) && static_cast(y) < static_cast(sizeY) && static_cast(z) < static_cast(sizeZ)); + if (!data) allocate(); + return &data[x + sizeX * z + sliceXZ * y]; + } + + int getSizeX() const { return sizeX; } + int getSizeY() const { return sizeY; } + int getSizeZ() const { return sizeZ; } + + Vector3int32 getSize() const { return Vector3int32(sizeX, sizeY, sizeZ); } + + bool isEmpty() const { return !data; } + + Box clone() const; + + private: + int sizeX; + int sizeY; + int sizeZ; + int sliceXZ; + + boost::shared_ptr data; + + static const Cell emptyCell; + + void allocate(); + }; + + class GridListener; + + class Grid + { + public: + Grid(); + + void connectListener(GridListener* listener); + void disconnectListener(GridListener* listener); + + Box read(const Region& region, int lod = 0) const; + void write(const Region& region, const Box& box); + + Cell getCell(int x, int y, int z) const; + + std::vector getNonEmptyRegions() const; + std::vector getNonEmptyRegionsInside(const Region& region) const; + + unsigned int getNonEmptyCellCountApprox() const; + + bool isAllocated() const { return !chunks.empty(); } + + void serialize(std::string& result) const; + void deserialize(const std::string& result); + + private: + enum { kChunkMips = 4 }; + + struct Chunk + { + Box data[kChunkMips]; + unsigned int volume; + + Chunk(); + + bool isEmpty() const; + }; + + boost::unordered_map chunks; + unsigned int chunksVolume; + + std::vector listeners; + }; + +} } diff --git a/App/voxel2/GridListener.h b/App/voxel2/GridListener.h new file mode 100644 index 0000000..dd5a602 --- /dev/null +++ b/App/voxel2/GridListener.h @@ -0,0 +1,15 @@ +#pragma once + +namespace RBX { namespace Voxel2 { + + class Region; + + class GridListener + { + public: + virtual ~GridListener() {} + + virtual void onTerrainRegionChanged(const Region& region) = 0; + }; + +} } diff --git a/App/voxel2/MaterialTable.h b/App/voxel2/MaterialTable.h new file mode 100644 index 0000000..9be2a27 --- /dev/null +++ b/App/voxel2/MaterialTable.h @@ -0,0 +1,80 @@ +#pragma once + +namespace RBX { namespace Voxel2 { + + class MaterialTable + { + public: + enum Type + { + Type_Soft, + Type_Hard, + Type_HardSoft, + }; + + enum Deformation + { + Deformation_None, + Deformation_Shift, + Deformation_Cubify, + Deformation_Quantize, + Deformation_Barrel, + Deformation_Water, + }; + + enum Mapping + { + Mapping_Default, + Mapping_Cube, + }; + + struct Material + { + std::string name; + + int topLayer; + int sideLayer; + int bottomLayer; + + Type type; + Mapping mapping; + + Deformation deformation; + float parameter; + }; + + struct Layer + { + float tiling; + float detiling; + }; + + struct Atlas + { + int width; + int height; + int tileSize; + int tileCount; + int borderSize; + }; + + MaterialTable(const std::string& file, unsigned int materialCount); + ~MaterialTable(); + + const Material& getMaterial(unsigned int index) const { return materials[index]; } + unsigned int getMaterialCount() const { return materials.size(); } + + const Layer& getLayer(unsigned int index) const { return layers[index]; } + unsigned int getLayerCount() const { return layers.size(); } + + const Atlas& getAtlas() const { return atlas; } + + private: + Atlas atlas; + std::vector materials; + std::vector layers; + + void load(const std::string& file); + }; + +} } diff --git a/App/voxel2/Mesher.h b/App/voxel2/Mesher.h new file mode 100644 index 0000000..f55e87b --- /dev/null +++ b/App/voxel2/Mesher.h @@ -0,0 +1,98 @@ +#pragma once + +#include "Util/G3DCore.h" +#include "Util/Vector3int32.h" + +#include "voxel2/Grid.h" + +namespace RBX { namespace Voxel2 { + + class MaterialTable; + + namespace Mesher + { + struct Vertex + { + Vector3 position; + + unsigned int border: 1; + unsigned int reserved: 7; + unsigned int material: 8; + unsigned int seed: 16; + }; + + struct GraphicsVertex + { + Vector3 position; + Color4uint8 normal; // xyz = normal, w = vertex index (0-2) + Color4uint8 material[3]; // x = layer index, y = normal segment (0-17), zw = random seed + }; + + struct GraphicsVertexPacked + { + Vector3int16 position; + short id; // vertex index (0-2) + Color4uint8 normal; // xyz = normal, w = random seed 0 + Color4uint8 material0; // xyz = layer index (0-?), w = random seed 1 + Color4uint8 material1; // xyz = normal segment (0-17), w = random seed 2 + }; + + struct Options + { + const MaterialTable* materials; + bool generateWater; + }; + + struct BasicMesh + { + std::vector vertices; + std::vector indices; + + static bool isWater(const Vertex& v0, const Vertex& v1, const Vertex& v2) + { + return (v0.material == Cell::Material_Water || v1.material == Cell::Material_Water || v2.material == Cell::Material_Water); + } + }; + + struct GraphicsMesh + { + std::vector vertices; + std::vector solidIndices; + std::vector waterIndices; + }; + + struct GraphicsMeshPacked + { + std::vector vertices; + std::vector solidIndices; + std::vector waterIndices; + }; + + void prepareTables(); + + BasicMesh generateGeometry(const Box& box, const Vector3int32& offset, int lod, const Options& options); + GraphicsMesh generateGraphicsGeometry(const BasicMesh& mesh, const Options& options); + + GraphicsMeshPacked generateGraphicsGeometryPacked(const BasicMesh& mesh, const Vector4& packInfo, const Options& options); + + struct TriangleAdjacency + { + enum + { + None = -1, + Multiple = -2 + }; + + int neighbor[3]; + }; + + void generateAdjacency(std::vector& result, const BasicMesh& mesh); + void generateEdgeFlags(std::vector& result, const BasicMesh& mesh, float cutoff); + + typedef const Vector3 TextureBasis[18]; + + const TextureBasis& getTextureBasisU(); + const TextureBasis& getTextureBasisV(); + }; + +} } diff --git a/CMakeLists.txt b/CMakeLists.txt index 4011d9a..2ae859a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ file(TO_CMAKE_PATH "${CONTRIB_PATH}" CONTRIB_PATH) message(STATUS "CONTRIB_PATH=${CONTRIB_PATH}") set(Boost_NO_SYSTEM_PATHS ON) -set(BOOST_ROOT ${CONTRIB_PATH}/boost_1_70_0) +set(BOOST_ROOT ${CONTRIB_PATH}/boost_1_55_0) set(Boost_INCLUDE_DIR ${BOOST_ROOT}/) include_directories(${Boost_INCLUDE_DIR}) string(TOLOWER ${CMAKE_BUILD_TYPE} RBX_BUILD_TYPE) @@ -30,6 +30,7 @@ add_compile_options( -Wno-c++11-narrowing -Wno-reserved-user-defined-literal -Wno-missing-field-initializers + -Wnonportable-include-path ) set(CMAKE_CXX_STANDARD 14) @@ -84,7 +85,6 @@ if(NOT RBX_SETTINGS_INITIALIZED) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall" CACHE STRING "" FORCE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unknown-pragmas -Wno-unused-variable -Wno-unused-local-typedefs -Wno-main" CACHE STRING "" FORCE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reorder -Wno-unused-local-typedefs" CACHE STRING "" FORCE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11" CACHE STRING "" FORCE) set(RBX_SETTINGS_INITIALIZED TRUE CACHE INTERNAL "" FORCE) endif() diff --git a/ClientShared/rapidjson/rapidjson.h b/ClientShared/rapidjson/rapidjson.h index 7acb2aa..1a18c32 100644 --- a/ClientShared/rapidjson/rapidjson.h +++ b/ClientShared/rapidjson/rapidjson.h @@ -268,7 +268,7 @@ public: // Realloc process: allocate and copy memory, do not free original buffer. void* newBuffer = Malloc(newSize); RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly. - return memcpy(newBuffer, originalPtr, originalSize); + return static_cast(memcpy(newBuffer, originalPtr, originalSize)); } //! Frees a memory block (concept Allocator) diff --git a/Network/Players.cpp b/Network/Players.cpp index b2ed813..ca1184d 100644 --- a/Network/Players.cpp +++ b/Network/Players.cpp @@ -2130,7 +2130,7 @@ void Players::onRemoteSysStats(int userId, const std::string& stat, const std::s if (willKick) { StandardOut::singleton()->printf(MESSAGE_INFO, "Players::onRemoteSysStats disconnect send failed"); // Remove the comment at the down if you already prepared your sysstats. - // disconnectPlayer(userId, Replicator::DisconnectReason_OnRemoteSysStats); + disconnectPlayer(userId, Replicator::DisconnectReason_OnRemoteSysStats); } return; } @@ -2139,7 +2139,7 @@ void Players::onRemoteSysStats(int userId, const std::string& stat, const std::s StandardOut::singleton()->printf(MESSAGE_INFO, "Players::onRemoteSysStats disconnect"); //Shut. It. Down. // Remove the comment at the down if you already prepared your sysstats. - // disconnectPlayer(userId, Replicator::DisconnectReason_OnRemoteSysStats); + disconnectPlayer(userId, Replicator::DisconnectReason_OnRemoteSysStats); } } diff --git a/Network/Replicator.RockyItem.cpp b/Network/Replicator.RockyItem.cpp index da69d2c..6cacc8e 100644 --- a/Network/Replicator.RockyItem.cpp +++ b/Network/Replicator.RockyItem.cpp @@ -37,7 +37,7 @@ bool Replicator::NetPmcResponseItem::write(RakNet::BitStream& bitStream) bitStream << static_cast(RockeyNetPmcResponse); bitStream << idx; bitStream << response; - bitStream << correct; + bitStream << static_cast(correct); return true; } diff --git a/Network/ServerReplicator.cpp b/Network/ServerReplicator.cpp index 77aeb26..2db9d3d 100644 --- a/Network/ServerReplicator.cpp +++ b/Network/ServerReplicator.cpp @@ -2093,16 +2093,16 @@ void CheatHandlingServerReplicator::processSendStats(unsigned int sendStats, uns doRemoteSysStats(maskedSendStats, HATE_IMPOSSIBLE_ERROR, "impala", "Impossible Error (31)"); if (maskedSendStats && ((maskedSendStats & HATE_IMPOSSIBLE_ERROR) == 0)) { - //doRemoteSysStats(maskedSendStats, HATE_CE_ASM, "robert", "WriteCopy changed (30)"); - //doRemoteSysStats(maskedSendStats, HATE_NEW_AV_CHECK, "moded", "Stealth Edit Revival (29)"); + doRemoteSysStats(maskedSendStats, HATE_CE_ASM, "robert", "WriteCopy changed (30)"); + doRemoteSysStats(maskedSendStats, HATE_NEW_AV_CHECK, "moded", "Stealth Edit Revival (29)"); doRemoteSysStats(maskedSendStats, HATE_HASH_FUNCTION_CHANGED, "booing", "Tried to modify hash function (28)"); doRemoteSysStats(maskedSendStats, HATE_RETURN_CHECK, "bobby", "Function Return Check Failed (27)"); doRemoteSysStats(maskedSendStats, HATE_VERB_SNATCH, "vera", "Tried to get build tools (26)"); - //doDelayedSysStats(maskedSendStats, HATE_VEH_HOOK, "vegah", "VEH used (25)"); + doDelayedSysStats(maskedSendStats, HATE_VEH_HOOK, "vegah", "VEH used (25)"); doRemoteSysStats(maskedSendStats, HATE_HSCE_HASH_CHANGED, "fisher", "HumanoidState::computeEvent changed (24)"); - //doDelayedSysStats(maskedSendStats, HATE_DLL_INJECTION, "dallas", "DLL Injection (23)"); + doDelayedSysStats(maskedSendStats, HATE_DLL_INJECTION, "dallas", "DLL Injection (23)"); doRemoteSysStats(maskedSendStats, HATE_INVALID_ENVIRONMENT, "tomy", "Sandbox or VM detected (22)"); doRemoteSysStats(maskedSendStats, HATE_SPEEDHACK, "usain", "Speedhack. (21)"); doRemoteSysStats(maskedSendStats, HATE_LUA_VM_HOOKED, "carol", "Lua vm hooked (20)"); @@ -2124,7 +2124,7 @@ void CheatHandlingServerReplicator::processSendStats(unsigned int sendStats, uns doRemoteSysStats(maskedSendStats, HATE_CONST_CHANGED, "lance", "Const Changed (7)"); doRemoteSysStats(maskedSendStats, HATE_INVALID_BYTECODE, "jack", "Invalid bytecode (6)"); - //doRemoteSysStats(maskedSendStats, HATE_MEMORY_HASH_CHANGED, "imogen", "Memory hash changed (5)"); + doRemoteSysStats(maskedSendStats, HATE_MEMORY_HASH_CHANGED, "imogen", "Memory hash changed (5)"); doRemoteSysStats(maskedSendStats, HATE_ILLEGAL_SCRIPTS, "ivan", "Illegal scripts (4)"); doRemoteSysStats(maskedSendStats, HATE_SIGNATURE, "omar", "Bad signature (3)"); diff --git a/Rendering/AppDraw/AppDraw.vcxproj b/Rendering/AppDraw/AppDraw.vcxproj index d379730..9c54de3 100644 --- a/Rendering/AppDraw/AppDraw.vcxproj +++ b/Rendering/AppDraw/AppDraw.vcxproj @@ -48,7 +48,7 @@ StaticLibrary - v140_xp + v110 MultiByte diff --git a/Rendering/AppDraw/CMakeFiles/AppDraw.dir/DependInfo.cmake b/Rendering/AppDraw/CMakeFiles/AppDraw.dir/DependInfo.cmake index 42b655d..adb00d6 100644 --- a/Rendering/AppDraw/CMakeFiles/AppDraw.dir/DependInfo.cmake +++ b/Rendering/AppDraw/CMakeFiles/AppDraw.dir/DependInfo.cmake @@ -26,9 +26,9 @@ SET(CMAKE_C_TARGET_INCLUDE_PATH "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include" "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include" "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward" - "/mnt/f/Trunk2012/Contribs/boost_1_70_0" + "/mnt/f/Trunk2012/Contribs/boost_1_55_0" "/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include" - "/mnt/f/Trunk2012/Contribs/boost_1_70_0/include" + "/mnt/f/Trunk2012/Contribs/boost_1_55_0/include" "fmod/include" "/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include" "/mnt/f/Trunk2012/Contribs/SDL2/include" diff --git a/Rendering/AppDraw/CMakeFiles/AppDraw.dir/flags.make b/Rendering/AppDraw/CMakeFiles/AppDraw.dir/flags.make index 988a101..37ce28a 100644 --- a/Rendering/AppDraw/CMakeFiles/AppDraw.dir/flags.make +++ b/Rendering/AppDraw/CMakeFiles/AppDraw.dir/flags.make @@ -2,7 +2,7 @@ # Generated by "Unix Makefiles" Generator, CMake Version 2.8 # compile CXX with /home/watrabi/Android/ndk/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++ -CXX_FLAGS = -fexceptions -frtti -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=neon -fdata-sections -ffunction-sections -Wa,--noexecstack -fexceptions -fpic -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-8/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fdata-sections -ffunction-sections -Wa,--noexecstack -fvisibility=hidden -mfloat-abi=softfp -MMD -MP -g -g -Wno-reorder -Wno-unused-local-typedefs -std=gnu++11 -mthumb -fomit-frame-pointer -fno-strict-aliasing -O3 -DNDEBUG -fPIC -isystem /home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm/usr/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I/mnt/f/Trunk2012/Contribs/boost_1_70_0 -I/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include -I/mnt/f/Trunk2012/Contribs/boost_1_70_0/include -I/mnt/f/Trunk2012/Client/fmod/include -I/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include -I/mnt/f/Trunk2012/Contribs/SDL2/include -I/mnt/f/Trunk2012/Client/Base/include -I/mnt/f/Trunk2012/Client/Log/include -I/mnt/f/Trunk2012/Client/Base/include/rbx/Android -I/mnt/f/Trunk2012/Client/App/include -I/mnt/f/Trunk2012/Client/App.BulletPhysics -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/include -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/../g3d/include -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/../RbxG3D/include -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/../GfxBase/include +CXX_FLAGS = -fexceptions -frtti -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=neon -fdata-sections -ffunction-sections -Wa,--noexecstack -fexceptions -fpic -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-8/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fdata-sections -ffunction-sections -Wa,--noexecstack -fvisibility=hidden -mfloat-abi=softfp -MMD -MP -g -g -Wno-reorder -Wno-unused-local-typedefs -std=gnu++11 -mthumb -fomit-frame-pointer -fno-strict-aliasing -O3 -DNDEBUG -fPIC -isystem /home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm/usr/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I/mnt/f/Trunk2012/Contribs/boost_1_55_0 -I/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include -I/mnt/f/Trunk2012/Contribs/boost_1_55_0/include -I/mnt/f/Trunk2012/Client/fmod/include -I/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include -I/mnt/f/Trunk2012/Contribs/SDL2/include -I/mnt/f/Trunk2012/Client/Base/include -I/mnt/f/Trunk2012/Client/Log/include -I/mnt/f/Trunk2012/Client/Base/include/rbx/Android -I/mnt/f/Trunk2012/Client/App/include -I/mnt/f/Trunk2012/Client/App.BulletPhysics -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/include -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/../g3d/include -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/../RbxG3D/include -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/../GfxBase/include CXX_DEFINES = -DANDROID -DROBLOX_BOOST_CONFIGS diff --git a/Rendering/GfxBase/CMakeFiles/GfxBase.dir/DependInfo.cmake b/Rendering/GfxBase/CMakeFiles/GfxBase.dir/DependInfo.cmake index 850fd30..cc339c8 100644 --- a/Rendering/GfxBase/CMakeFiles/GfxBase.dir/DependInfo.cmake +++ b/Rendering/GfxBase/CMakeFiles/GfxBase.dir/DependInfo.cmake @@ -37,9 +37,9 @@ SET(CMAKE_C_TARGET_INCLUDE_PATH "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include" "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include" "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward" - "/mnt/f/Trunk2012/Contribs/boost_1_70_0" + "/mnt/f/Trunk2012/Contribs/boost_1_55_0" "/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include" - "/mnt/f/Trunk2012/Contribs/boost_1_70_0/include" + "/mnt/f/Trunk2012/Contribs/boost_1_55_0/include" "fmod/include" "/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include" "/mnt/f/Trunk2012/Contribs/SDL2/include" diff --git a/Rendering/GfxBase/CMakeFiles/GfxBase.dir/flags.make b/Rendering/GfxBase/CMakeFiles/GfxBase.dir/flags.make index 10e6cc9..487399c 100644 --- a/Rendering/GfxBase/CMakeFiles/GfxBase.dir/flags.make +++ b/Rendering/GfxBase/CMakeFiles/GfxBase.dir/flags.make @@ -2,7 +2,7 @@ # Generated by "Unix Makefiles" Generator, CMake Version 2.8 # compile CXX with /home/watrabi/Android/ndk/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++ -CXX_FLAGS = -fexceptions -frtti -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=neon -fdata-sections -ffunction-sections -Wa,--noexecstack -fexceptions -fpic -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-8/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fdata-sections -ffunction-sections -Wa,--noexecstack -fvisibility=hidden -mfloat-abi=softfp -MMD -MP -g -g -Wno-reorder -Wno-unused-local-typedefs -std=gnu++11 -mthumb -fomit-frame-pointer -fno-strict-aliasing -O3 -DNDEBUG -fPIC -isystem /home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm/usr/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I/mnt/f/Trunk2012/Contribs/boost_1_70_0 -I/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include -I/mnt/f/Trunk2012/Contribs/boost_1_70_0/include -I/mnt/f/Trunk2012/Client/fmod/include -I/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include -I/mnt/f/Trunk2012/Contribs/SDL2/include -I/mnt/f/Trunk2012/Client/Base/include -I/mnt/f/Trunk2012/Client/Log/include -I/mnt/f/Trunk2012/Client/Base/include/rbx/Android -I/mnt/f/Trunk2012/Client/App/include -I/mnt/f/Trunk2012/Client/App.BulletPhysics -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/include -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/../g3d/include -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/../RbxG3d/include -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/../AppDraw/include +CXX_FLAGS = -fexceptions -frtti -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=neon -fdata-sections -ffunction-sections -Wa,--noexecstack -fexceptions -fpic -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-8/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fdata-sections -ffunction-sections -Wa,--noexecstack -fvisibility=hidden -mfloat-abi=softfp -MMD -MP -g -g -Wno-reorder -Wno-unused-local-typedefs -std=gnu++11 -mthumb -fomit-frame-pointer -fno-strict-aliasing -O3 -DNDEBUG -fPIC -isystem /home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm/usr/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I/mnt/f/Trunk2012/Contribs/boost_1_55_0 -I/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include -I/mnt/f/Trunk2012/Contribs/boost_1_55_0/include -I/mnt/f/Trunk2012/Client/fmod/include -I/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include -I/mnt/f/Trunk2012/Contribs/SDL2/include -I/mnt/f/Trunk2012/Client/Base/include -I/mnt/f/Trunk2012/Client/Log/include -I/mnt/f/Trunk2012/Client/Base/include/rbx/Android -I/mnt/f/Trunk2012/Client/App/include -I/mnt/f/Trunk2012/Client/App.BulletPhysics -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/include -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/../g3d/include -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/../RbxG3d/include -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/../AppDraw/include CXX_DEFINES = -DANDROID -DROBLOX_BOOST_CONFIGS diff --git a/Rendering/GfxBase/GfxBase.vcxproj b/Rendering/GfxBase/GfxBase.vcxproj index c34e12c..bf0432c 100644 --- a/Rendering/GfxBase/GfxBase.vcxproj +++ b/Rendering/GfxBase/GfxBase.vcxproj @@ -48,7 +48,7 @@ StaticLibrary - v140_xp + v110 MultiByte diff --git a/Rendering/GfxCore/CMakeFiles/GfxCore.dir/flags.make b/Rendering/GfxCore/CMakeFiles/GfxCore.dir/flags.make index 40c2e4e..75a3fda 100644 --- a/Rendering/GfxCore/CMakeFiles/GfxCore.dir/flags.make +++ b/Rendering/GfxCore/CMakeFiles/GfxCore.dir/flags.make @@ -2,7 +2,7 @@ # Generated by "Unix Makefiles" Generator, CMake Version 2.8 # compile CXX with /home/watrabi/Android/ndk/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++ -CXX_FLAGS = -fexceptions -frtti -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=neon -fdata-sections -ffunction-sections -Wa,--noexecstack -fexceptions -fpic -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-8/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fdata-sections -ffunction-sections -Wa,--noexecstack -fvisibility=hidden -mfloat-abi=softfp -MMD -MP -g -g -Wno-reorder -Wno-unused-local-typedefs -std=gnu++11 -mthumb -fomit-frame-pointer -fno-strict-aliasing -O3 -DNDEBUG -fPIC -isystem /home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm/usr/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I/mnt/f/Trunk2012/Contribs/boost_1_70_0 -I/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include -I/mnt/f/Trunk2012/Contribs/boost_1_70_0/include -I/mnt/f/Trunk2012/Client/fmod/include -I/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include -I/mnt/f/Trunk2012/Contribs/SDL2/include -I/mnt/f/Trunk2012/Client/Base/include -I/mnt/f/Trunk2012/Client/Log/include -I/mnt/f/Trunk2012/Client/Base/include/rbx/Android -I/mnt/f/Trunk2012/Client/App/include -I/mnt/f/Trunk2012/Client/App.BulletPhysics -I/mnt/f/Trunk2012/Client/Rendering/g3d/include -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/include -I/mnt/f/Trunk2012/Client/Rendering/RbxG3D/include -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/include -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/GL -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/include -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/glew +CXX_FLAGS = -fexceptions -frtti -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=neon -fdata-sections -ffunction-sections -Wa,--noexecstack -fexceptions -fpic -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-8/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fdata-sections -ffunction-sections -Wa,--noexecstack -fvisibility=hidden -mfloat-abi=softfp -MMD -MP -g -g -Wno-reorder -Wno-unused-local-typedefs -std=gnu++11 -mthumb -fomit-frame-pointer -fno-strict-aliasing -O3 -DNDEBUG -fPIC -isystem /home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm/usr/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I/mnt/f/Trunk2012/Contribs/boost_1_55_0 -I/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include -I/mnt/f/Trunk2012/Contribs/boost_1_55_0/include -I/mnt/f/Trunk2012/Client/fmod/include -I/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include -I/mnt/f/Trunk2012/Contribs/SDL2/include -I/mnt/f/Trunk2012/Client/Base/include -I/mnt/f/Trunk2012/Client/Log/include -I/mnt/f/Trunk2012/Client/Base/include/rbx/Android -I/mnt/f/Trunk2012/Client/App/include -I/mnt/f/Trunk2012/Client/App.BulletPhysics -I/mnt/f/Trunk2012/Client/Rendering/g3d/include -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/include -I/mnt/f/Trunk2012/Client/Rendering/RbxG3D/include -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/include -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/GL -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/include -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/glew CXX_DEFINES = -DANDROID -DGLEW_NO_GLU -DROBLOX_BOOST_CONFIGS diff --git a/Rendering/GfxCore/CMakeFiles/GfxCore_unbuilt.dir/DependInfo.cmake b/Rendering/GfxCore/CMakeFiles/GfxCore_unbuilt.dir/DependInfo.cmake index e65316d..f798160 100644 --- a/Rendering/GfxCore/CMakeFiles/GfxCore_unbuilt.dir/DependInfo.cmake +++ b/Rendering/GfxCore/CMakeFiles/GfxCore_unbuilt.dir/DependInfo.cmake @@ -20,9 +20,9 @@ SET(CMAKE_C_TARGET_INCLUDE_PATH "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include" "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include" "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward" - "/mnt/f/Trunk2012/Contribs/boost_1_70_0" + "/mnt/f/Trunk2012/Contribs/boost_1_55_0" "/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include" - "/mnt/f/Trunk2012/Contribs/boost_1_70_0/include" + "/mnt/f/Trunk2012/Contribs/boost_1_55_0/include" "fmod/include" "/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include" "/mnt/f/Trunk2012/Contribs/SDL2/include" diff --git a/Rendering/GfxCore/GfxCore.vcxproj b/Rendering/GfxCore/GfxCore.vcxproj index efbde40..0d45382 100644 --- a/Rendering/GfxCore/GfxCore.vcxproj +++ b/Rendering/GfxCore/GfxCore.vcxproj @@ -39,7 +39,7 @@ StaticLibrary - v140_xp + v110 MultiByte false diff --git a/Rendering/GfxCore/GfxCore.xcodeproj/project.pbxproj b/Rendering/GfxCore/GfxCore.xcodeproj/project.pbxproj index 2b0f050..95f077f 100644 --- a/Rendering/GfxCore/GfxCore.xcodeproj/project.pbxproj +++ b/Rendering/GfxCore/GfxCore.xcodeproj/project.pbxproj @@ -402,7 +402,7 @@ ../GfxBase/include/GfxBase, ../G3D/include, ../RBXG3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", glew, ); MACOSX_DEPLOYMENT_TARGET = 10.6; @@ -429,7 +429,7 @@ ../GfxBase/include/GfxBase, ../G3D/include, ../RBXG3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", glew, ); MACOSX_DEPLOYMENT_TARGET = 10.6; @@ -464,7 +464,7 @@ ../GfxBase/include/GfxBase, ../G3D/include, ../RBXG3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", ); IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; ONLY_ACTIVE_ARCH = NO; @@ -506,7 +506,7 @@ ../GfxBase/include/GfxBase, ../G3D/include, ../RBXG3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", ); IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; ONLY_ACTIVE_ARCH = NO; @@ -577,7 +577,7 @@ ../GfxBase/include/GfxBase, ../G3D/include, ../RBXG3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", glew, ); MACOSX_DEPLOYMENT_TARGET = 10.6; @@ -612,7 +612,7 @@ ../GfxBase/include/GfxBase, ../G3D/include, ../RBXG3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", ); IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; ONLY_ACTIVE_ARCH = NO; diff --git a/Rendering/GfxRender/CMakeFiles/GfxRender.dir/DependInfo.cmake b/Rendering/GfxRender/CMakeFiles/GfxRender.dir/DependInfo.cmake index 304cc89..b355d73 100644 --- a/Rendering/GfxRender/CMakeFiles/GfxRender.dir/DependInfo.cmake +++ b/Rendering/GfxRender/CMakeFiles/GfxRender.dir/DependInfo.cmake @@ -110,9 +110,9 @@ SET(CMAKE_C_TARGET_INCLUDE_PATH "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include" "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include" "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward" - "/mnt/f/Trunk2012/Contribs/boost_1_70_0" + "/mnt/f/Trunk2012/Contribs/boost_1_55_0" "/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include" - "/mnt/f/Trunk2012/Contribs/boost_1_70_0/include" + "/mnt/f/Trunk2012/Contribs/boost_1_55_0/include" "fmod/include" "/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include" "/mnt/f/Trunk2012/Contribs/SDL2/include" diff --git a/Rendering/GfxRender/CMakeFiles/GfxRender.dir/flags.make b/Rendering/GfxRender/CMakeFiles/GfxRender.dir/flags.make index 7eccd3d..24cc413 100644 --- a/Rendering/GfxRender/CMakeFiles/GfxRender.dir/flags.make +++ b/Rendering/GfxRender/CMakeFiles/GfxRender.dir/flags.make @@ -3,11 +3,11 @@ # compile C with /home/watrabi/Android/ndk/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc # compile CXX with /home/watrabi/Android/ndk/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++ -C_FLAGS = -fexceptions -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=neon -fdata-sections -ffunction-sections -Wa,--noexecstack -fexceptions -fpic -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-8/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fdata-sections -ffunction-sections -Wa,--noexecstack -fvisibility=hidden -mfloat-abi=softfp -MMD -MP -g -Wall -Wno-unknown-pragmas -Wno-unused-variable -Wno-unused-local-typedefs -Wno-main -mthumb -fomit-frame-pointer -fno-strict-aliasing -O3 -DNDEBUG -fPIC -isystem /home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm/usr/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I/mnt/f/Trunk2012/Contribs/boost_1_70_0 -I/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include -I/mnt/f/Trunk2012/Contribs/boost_1_70_0/include -I/mnt/f/Trunk2012/Client/fmod/include -I/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include -I/mnt/f/Trunk2012/Contribs/SDL2/include -I/mnt/f/Trunk2012/Client/Base/include -I/mnt/f/Trunk2012/Client/Log/include -I/mnt/f/Trunk2012/Client/Base/include/rbx/Android -I/mnt/f/Trunk2012/Client/App/include -I/mnt/f/Trunk2012/Client/App.BulletPhysics -I/mnt/f/Trunk2012/Client/Rendering/g3d/include -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/include -I/mnt/f/Trunk2012/Client/Rendering/RbxG3D/include -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/include -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/GL -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/include -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/../GfxAdapters -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/../../ClientShared -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/freetype/include -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/freetype/src +C_FLAGS = -fexceptions -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=neon -fdata-sections -ffunction-sections -Wa,--noexecstack -fexceptions -fpic -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-8/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fdata-sections -ffunction-sections -Wa,--noexecstack -fvisibility=hidden -mfloat-abi=softfp -MMD -MP -g -Wall -Wno-unknown-pragmas -Wno-unused-variable -Wno-unused-local-typedefs -Wno-main -mthumb -fomit-frame-pointer -fno-strict-aliasing -O3 -DNDEBUG -fPIC -isystem /home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm/usr/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I/mnt/f/Trunk2012/Contribs/boost_1_55_0 -I/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include -I/mnt/f/Trunk2012/Contribs/boost_1_55_0/include -I/mnt/f/Trunk2012/Client/fmod/include -I/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include -I/mnt/f/Trunk2012/Contribs/SDL2/include -I/mnt/f/Trunk2012/Client/Base/include -I/mnt/f/Trunk2012/Client/Log/include -I/mnt/f/Trunk2012/Client/Base/include/rbx/Android -I/mnt/f/Trunk2012/Client/App/include -I/mnt/f/Trunk2012/Client/App.BulletPhysics -I/mnt/f/Trunk2012/Client/Rendering/g3d/include -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/include -I/mnt/f/Trunk2012/Client/Rendering/RbxG3D/include -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/include -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/GL -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/include -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/../GfxAdapters -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/../../ClientShared -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/freetype/include -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/freetype/src C_DEFINES = -DANDROID -DFT2_BUILD_LIBRARY -DGLEW_NO_GLU -DROBLOX_BOOST_CONFIGS -CXX_FLAGS = -fexceptions -frtti -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=neon -fdata-sections -ffunction-sections -Wa,--noexecstack -fexceptions -fpic -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-8/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fdata-sections -ffunction-sections -Wa,--noexecstack -fvisibility=hidden -mfloat-abi=softfp -MMD -MP -g -g -Wno-reorder -Wno-unused-local-typedefs -std=gnu++11 -mthumb -fomit-frame-pointer -fno-strict-aliasing -O3 -DNDEBUG -fPIC -isystem /home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm/usr/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I/mnt/f/Trunk2012/Contribs/boost_1_70_0 -I/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include -I/mnt/f/Trunk2012/Contribs/boost_1_70_0/include -I/mnt/f/Trunk2012/Client/fmod/include -I/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include -I/mnt/f/Trunk2012/Contribs/SDL2/include -I/mnt/f/Trunk2012/Client/Base/include -I/mnt/f/Trunk2012/Client/Log/include -I/mnt/f/Trunk2012/Client/Base/include/rbx/Android -I/mnt/f/Trunk2012/Client/App/include -I/mnt/f/Trunk2012/Client/App.BulletPhysics -I/mnt/f/Trunk2012/Client/Rendering/g3d/include -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/include -I/mnt/f/Trunk2012/Client/Rendering/RbxG3D/include -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/include -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/GL -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/include -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/../GfxAdapters -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/../../ClientShared -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/freetype/include -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/freetype/src +CXX_FLAGS = -fexceptions -frtti -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=neon -fdata-sections -ffunction-sections -Wa,--noexecstack -fexceptions -fpic -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-8/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fdata-sections -ffunction-sections -Wa,--noexecstack -fvisibility=hidden -mfloat-abi=softfp -MMD -MP -g -g -Wno-reorder -Wno-unused-local-typedefs -std=gnu++11 -mthumb -fomit-frame-pointer -fno-strict-aliasing -O3 -DNDEBUG -fPIC -isystem /home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm/usr/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I/mnt/f/Trunk2012/Contribs/boost_1_55_0 -I/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include -I/mnt/f/Trunk2012/Contribs/boost_1_55_0/include -I/mnt/f/Trunk2012/Client/fmod/include -I/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include -I/mnt/f/Trunk2012/Contribs/SDL2/include -I/mnt/f/Trunk2012/Client/Base/include -I/mnt/f/Trunk2012/Client/Log/include -I/mnt/f/Trunk2012/Client/Base/include/rbx/Android -I/mnt/f/Trunk2012/Client/App/include -I/mnt/f/Trunk2012/Client/App.BulletPhysics -I/mnt/f/Trunk2012/Client/Rendering/g3d/include -I/mnt/f/Trunk2012/Client/Rendering/GfxBase/include -I/mnt/f/Trunk2012/Client/Rendering/RbxG3D/include -I/mnt/f/Trunk2012/Client/Rendering/AppDraw/include -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/GL -I/mnt/f/Trunk2012/Client/Rendering/GfxCore/include -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/../GfxAdapters -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/../../ClientShared -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/freetype/include -I/mnt/f/Trunk2012/Client/Rendering/GfxRender/freetype/src CXX_DEFINES = -DANDROID -DFT2_BUILD_LIBRARY -DGLEW_NO_GLU -DROBLOX_BOOST_CONFIGS diff --git a/Rendering/GfxRender/GfxRender.vcxproj b/Rendering/GfxRender/GfxRender.vcxproj index 502876a..ea2410b 100644 --- a/Rendering/GfxRender/GfxRender.vcxproj +++ b/Rendering/GfxRender/GfxRender.vcxproj @@ -543,7 +543,7 @@ StaticLibrary - v140_xp + v110 MultiByte false diff --git a/Rendering/GfxRender/GfxRender.xcodeproj/project.pbxproj b/Rendering/GfxRender/GfxRender.xcodeproj/project.pbxproj index 8b014f9..9ce8543 100644 --- a/Rendering/GfxRender/GfxRender.xcodeproj/project.pbxproj +++ b/Rendering/GfxRender/GfxRender.xcodeproj/project.pbxproj @@ -1497,7 +1497,7 @@ ../GfxBase/include/GfxBase, ../G3D/include, ../RBXG3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", ../../App.BulletPhysics, "$(CONTRIB_PATH)/SDL2.0.4/include", ); @@ -1532,7 +1532,7 @@ ../GfxBase/include/GfxBase, ../G3D/include, ../RBXG3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", ../../App.BulletPhysics, "$(CONTRIB_PATH)/SDL2.0.4/include", ); @@ -1568,7 +1568,7 @@ ../GfxBase/include/GfxBase, ../G3D/include, ../RBXG3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", ../../App.BulletPhysics, freetype/src, freetype/include, @@ -1614,7 +1614,7 @@ ../GfxBase/include/GfxBase, ../G3D/include, ../RBXG3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", ../../App.BulletPhysics, "$(CONTRIB_PATH)/SDL2.0.4/include", freetype/include, @@ -1695,7 +1695,7 @@ ../GfxBase/include/GfxBase, ../G3D/include, ../RBXG3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", ../../App.BulletPhysics, "$(CONTRIB_PATH)/SDL2.0.4/include", freetype/src, @@ -1734,7 +1734,7 @@ ../GfxBase/include/GfxBase, ../G3D/include, ../RBXG3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", ../../App.BulletPhysics, freetype/src, freetype/include, diff --git a/Rendering/RbxG3D/CMakeFiles/RbxG3D.dir/DependInfo.cmake b/Rendering/RbxG3D/CMakeFiles/RbxG3D.dir/DependInfo.cmake index 1b48ab3..1638c4a 100644 --- a/Rendering/RbxG3D/CMakeFiles/RbxG3D.dir/DependInfo.cmake +++ b/Rendering/RbxG3D/CMakeFiles/RbxG3D.dir/DependInfo.cmake @@ -26,9 +26,9 @@ SET(CMAKE_C_TARGET_INCLUDE_PATH "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include" "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include" "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward" - "/mnt/f/Trunk2012/Contribs/boost_1_70_0" + "/mnt/f/Trunk2012/Contribs/boost_1_55_0" "/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include" - "/mnt/f/Trunk2012/Contribs/boost_1_70_0/include" + "/mnt/f/Trunk2012/Contribs/boost_1_55_0/include" "fmod/include" "/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include" "/mnt/f/Trunk2012/Contribs/SDL2/include" diff --git a/Rendering/RbxG3D/CMakeFiles/RbxG3D.dir/flags.make b/Rendering/RbxG3D/CMakeFiles/RbxG3D.dir/flags.make index 1efb6e7..2681000 100644 --- a/Rendering/RbxG3D/CMakeFiles/RbxG3D.dir/flags.make +++ b/Rendering/RbxG3D/CMakeFiles/RbxG3D.dir/flags.make @@ -2,7 +2,7 @@ # Generated by "Unix Makefiles" Generator, CMake Version 2.8 # compile CXX with /home/watrabi/Android/ndk/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++ -CXX_FLAGS = -fexceptions -frtti -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=neon -fdata-sections -ffunction-sections -Wa,--noexecstack -fexceptions -fpic -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-8/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fdata-sections -ffunction-sections -Wa,--noexecstack -fvisibility=hidden -mfloat-abi=softfp -MMD -MP -g -g -Wno-reorder -Wno-unused-local-typedefs -std=gnu++11 -mthumb -fomit-frame-pointer -fno-strict-aliasing -O3 -DNDEBUG -fPIC -isystem /home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm/usr/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I/mnt/f/Trunk2012/Contribs/boost_1_70_0 -I/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include -I/mnt/f/Trunk2012/Contribs/boost_1_70_0/include -I/mnt/f/Trunk2012/Client/fmod/include -I/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include -I/mnt/f/Trunk2012/Contribs/SDL2/include -I/mnt/f/Trunk2012/Client/Base/include -I/mnt/f/Trunk2012/Client/Log/include -I/mnt/f/Trunk2012/Client/Base/include/rbx/Android -I/mnt/f/Trunk2012/Client/App/include -I/mnt/f/Trunk2012/Client/App.BulletPhysics -I/mnt/f/Trunk2012/Client/Rendering/RbxG3D/include -I/mnt/f/Trunk2012/Client/Rendering/RbxG3D/../g3d/include +CXX_FLAGS = -fexceptions -frtti -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=neon -fdata-sections -ffunction-sections -Wa,--noexecstack -fexceptions -fpic -Wno-psabi --sysroot=/home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-8/arch-arm -funwind-tables -finline-limit=64 -fsigned-char -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fdata-sections -ffunction-sections -Wa,--noexecstack -fvisibility=hidden -mfloat-abi=softfp -MMD -MP -g -g -Wno-reorder -Wno-unused-local-typedefs -std=gnu++11 -mthumb -fomit-frame-pointer -fno-strict-aliasing -O3 -DNDEBUG -fPIC -isystem /home/watrabi/Android/ndk/android-ndk-r10e/platforms/android-21/arch-arm/usr/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -isystem /home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -I/mnt/f/Trunk2012/Contribs/boost_1_55_0 -I/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include -I/mnt/f/Trunk2012/Contribs/boost_1_55_0/include -I/mnt/f/Trunk2012/Client/fmod/include -I/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include -I/mnt/f/Trunk2012/Contribs/SDL2/include -I/mnt/f/Trunk2012/Client/Base/include -I/mnt/f/Trunk2012/Client/Log/include -I/mnt/f/Trunk2012/Client/Base/include/rbx/Android -I/mnt/f/Trunk2012/Client/App/include -I/mnt/f/Trunk2012/Client/App.BulletPhysics -I/mnt/f/Trunk2012/Client/Rendering/RbxG3D/include -I/mnt/f/Trunk2012/Client/Rendering/RbxG3D/../g3d/include CXX_DEFINES = -DANDROID -DROBLOX_BOOST_CONFIGS diff --git a/Rendering/RbxG3D/CMakeFiles/RbxG3D_unbuilt.dir/DependInfo.cmake b/Rendering/RbxG3D/CMakeFiles/RbxG3D_unbuilt.dir/DependInfo.cmake index 38262cc..8b6cf90 100644 --- a/Rendering/RbxG3D/CMakeFiles/RbxG3D_unbuilt.dir/DependInfo.cmake +++ b/Rendering/RbxG3D/CMakeFiles/RbxG3D_unbuilt.dir/DependInfo.cmake @@ -19,9 +19,9 @@ SET(CMAKE_C_TARGET_INCLUDE_PATH "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include" "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include" "/home/watrabi/Android/ndk/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.9/include/backward" - "/mnt/f/Trunk2012/Contribs/boost_1_70_0" + "/mnt/f/Trunk2012/Contribs/boost_1_55_0" "/mnt/f/Trunk2012/Contribs/android/armeabi-v7a/curl/curl-7.43.0/include" - "/mnt/f/Trunk2012/Contribs/boost_1_70_0/include" + "/mnt/f/Trunk2012/Contribs/boost_1_55_0/include" "fmod/include" "/mnt/f/Trunk2012/Contribs/android/arm/openssl/openssl-1.0.2c/include" "/mnt/f/Trunk2012/Contribs/SDL2/include" diff --git a/Rendering/RbxG3D/RbxG3D.xcodeproj/project.pbxproj b/Rendering/RbxG3D/RbxG3D.xcodeproj/project.pbxproj index 0aee552..d89c548 100644 --- a/Rendering/RbxG3D/RbxG3D.xcodeproj/project.pbxproj +++ b/Rendering/RbxG3D/RbxG3D.xcodeproj/project.pbxproj @@ -238,7 +238,7 @@ ../GfxBase/include, ../GfxBase/include/GfxBase, ../G3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", "$(CONTRIB_PATH)/GeekInfo/geekinfo-2.1.4/include", ../../log/include, ../../App.BulletPhysics, @@ -264,7 +264,7 @@ ../GfxBase/include, ../GfxBase/include/GfxBase, ../G3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", "$(CONTRIB_PATH)/GeekInfo/geekinfo-2.1.4/include", ../../log/include, ../../App.BulletPhysics, @@ -298,7 +298,7 @@ ../GfxBase/include, ../GfxBase/include/GfxBase, ../G3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", "$(CONTRIB_PATH)/GeekInfo/geekinfo-2.1.4/include", ../../App.BulletPhysics, ); @@ -334,7 +334,7 @@ ../GfxBase/include, ../GfxBase/include/GfxBase, ../G3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", "$(CONTRIB_PATH)/GeekInfo/geekinfo-2.1.4/include", ../../App.BulletPhysics, ); @@ -370,7 +370,7 @@ ../GfxBase/include, ../GfxBase/include/GfxBase, ../G3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", "$(CONTRIB_PATH)/GeekInfo/geekinfo-2.1.4/include", ../../log/include, ../../App.BulletPhysics, @@ -408,7 +408,7 @@ ../GfxBase/include, ../GfxBase/include/GfxBase, ../G3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", "$(CONTRIB_PATH)/GeekInfo/geekinfo-2.1.4/include", ../../log/include, ../../App.BulletPhysics, @@ -451,7 +451,7 @@ ../GfxBase/include, ../GfxBase/include/GfxBase, ../G3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", "$(CONTRIB_PATH)/GeekInfo/geekinfo-2.1.4/include", ../../App.BulletPhysics, ); @@ -478,7 +478,7 @@ ../GfxBase/include, ../GfxBase/include/GfxBase, ../G3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", "$(CONTRIB_PATH)/GeekInfo/geekinfo-2.1.4/include", ../../log/include, ../../App.BulletPhysics, @@ -511,7 +511,7 @@ ../GfxBase/include, ../GfxBase/include/GfxBase, ../G3D/include, - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", "$(CONTRIB_PATH)/GeekInfo/geekinfo-2.1.4/include", ../../log/include, ../../App.BulletPhysics, diff --git a/RobloxStudio/RobloxStudio.xcodeproj/project.pbxproj b/RobloxStudio/RobloxStudio.xcodeproj/project.pbxproj index 36850bb..9e62cf9 100644 --- a/RobloxStudio/RobloxStudio.xcodeproj/project.pbxproj +++ b/RobloxStudio/RobloxStudio.xcodeproj/project.pbxproj @@ -2423,7 +2423,7 @@ "$(QT_DIR_PATH)/Frameworks/QtXml.framework/Headers", "$(QT_DIR_PATH)/Frameworks/QtNetwork.framework/Headers", "$(QT_DIR_PATH)/usr/local/mkspecs/macx-xcode", - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", "$(CONTRIB_PATH)/google-breakpad/19OCT2015/src/client/mac/Framework", "$(CONTRIB_PATH)/google-breakpad/19OCT2015/src/client/apple/Framework", ../CSG/sgCore, @@ -2541,7 +2541,7 @@ "$(QT_DIR_PATH)/Frameworks/QtXml.framework/Headers", "$(QT_DIR_PATH)/Frameworks/QtNetwork.framework/Headers", "$(QT_DIR_PATH)/usr/local/mkspecs/macx-xcode", - "$(CONTRIB_PATH)/boost_1_70_0/include", + "$(CONTRIB_PATH)/boost_1_55_0/include", "$(CONTRIB_PATH)/google-breakpad/19OCT2015/src/client/mac/Framework", "$(CONTRIB_PATH)/google-breakpad/19OCT2015/src/client/apple/Framework", ../CSG/sgCore, diff --git a/WindowsClient/Application.cpp b/WindowsClient/Application.cpp index 39c6bea..b884212 100644 --- a/WindowsClient/Application.cpp +++ b/WindowsClient/Application.cpp @@ -1074,16 +1074,6 @@ bool Application::ParseArguments(const char* argv) #endif } - if (vm.count("app")) { - if (auto dm = currentDocument->getGame()->getDataModel()) { - dm->submitTask(boost::bind(&Document::Start, currentDocument.get(), boost::make_shared_future(std::string()), SharedLauncher::Play, false, getVRDeviceName()), DataModelJob::Write); - dm->setIsXboxApp(true); - dm->startCoreScripts(true, "XStarterScript"); - dm->loadContent(ContentId("rbxasset://ScaledWorldv4.7.rbxl")); - } - launchMode = SharedLauncher::Play; - } - // used to determine how we will initialize datamodel if (vm.count("play")) launchMode = SharedLauncher::Play; diff --git a/WindowsClient/UserInput.cpp b/WindowsClient/UserInput.cpp index 65a7836..cb57d0a 100644 --- a/WindowsClient/UserInput.cpp +++ b/WindowsClient/UserInput.cpp @@ -345,7 +345,7 @@ void UserInput::acquireKeyboard() diKeyboardPtr->Unacquire(); // for good measure HRESULT hr = diKeyboardPtr->SetCooperativeLevel(wnd, - DISCL_FOREGROUND | DISCL_EXCLUSIVE); + DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); if (hr != DI_OK) { DXINPUT_TRACE("diKeyboardPtr->SetCooperativeLevel failed %d\n", hr); diff --git a/WindowsClient/WindowsClient.vcxproj b/WindowsClient/WindowsClient.vcxproj index 623ed1c..562101a 100644 --- a/WindowsClient/WindowsClient.vcxproj +++ b/WindowsClient/WindowsClient.vcxproj @@ -187,6 +187,7 @@ true $(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir_71A)lib;$(CONTRIB_PATH)\boost_1_56_0\lib; + $(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSDK_LibraryPath_x86);$(CONTRIB_PATH)\boost_1_56_0\lib; $(Platform)\$(Configuration)\ @@ -986,7 +987,6 @@ echo placeholder > ..\WindowsClient\$(Platform)\$(Configuration)\boost.pdb - diff --git a/WindowsClient/WindowsClient.vcxproj.filters b/WindowsClient/WindowsClient.vcxproj.filters index 0a42abc..2cd3647 100644 --- a/WindowsClient/WindowsClient.vcxproj.filters +++ b/WindowsClient/WindowsClient.vcxproj.filters @@ -214,9 +214,6 @@ Header Files - - Header Files - diff --git a/cmake/Modules/Boost.cmake b/cmake/Modules/Boost.cmake index a74bba9..8db994d 100644 --- a/cmake/Modules/Boost.cmake +++ b/cmake/Modules/Boost.cmake @@ -1,2 +1,2 @@ -file(TO_CMAKE_PATH "${CONTRIB_PATH}/boost_1_70_0" boost_ROOT) +file(TO_CMAKE_PATH "${CONTRIB_PATH}/boost_1_55_0" boost_ROOT) include_directories("${boost_ROOT}/include") diff --git a/cmake/test-android.sh b/cmake/test-android.sh index 2474ba2..a9d9f99 100644 --- a/cmake/test-android.sh +++ b/cmake/test-android.sh @@ -24,7 +24,7 @@ COMMON_CMAKE_ARGS="$COMMON_CMAKE_ARGS -DANDROID_ABI=arm64-v8a" COMMON_CMAKE_ARGS="$COMMON_CMAKE_ARGS -DANDROID_PLATFORM=android-${ANDROID_API}" COMMON_CMAKE_ARGS="$COMMON_CMAKE_ARGS -DANDROID_STL=c++_static" COMMON_CMAKE_ARGS="$COMMON_CMAKE_ARGS -DCONTRIB_PATH='${CONTRIB_PATH}'" -COMMON_CMAKE_ARGS="$COMMON_CMAKE_ARGS -DBOOST_ROOT='${CONTRIB_PATH}/boost_1_70_0/'" +COMMON_CMAKE_ARGS="$COMMON_CMAKE_ARGS -DBOOST_ROOT='${CONTRIB_PATH}/boost_1_55_0/'" COMMON_CMAKE_ARGS="$COMMON_CMAKE_ARGS -DRBX_PLATFORM_ANDROID=ON" COMMON_CMAKE_ARGS="$COMMON_CMAKE_ARGS -DCMAKE_CXX_FLAGS='-DRBX_PLATFORM_ANDROID'"