diff --git a/autogen/src/analysis.cpp b/autogen/src/analysis.cpp index bdd813a..ed24692 100644 --- a/autogen/src/analysis.cpp +++ b/autogen/src/analysis.cpp @@ -137,10 +137,17 @@ std::string findBaseClass(CXCursor cur) { return baseClass; } +std::string currentCategory = ""; void processField(CXCursor cur, ClassAnalysis* state) { std::optional propertyDef = findAnnotation(cur, "OB::def_prop"); if (!propertyDef) return; + // Update current category + std::optional categoryDef = findAnnotation(cur, "OB::def_prop_category"); + if (categoryDef) { + currentCategory = parseAnnotationString(categoryDef.value())["category"]; + } + PropertyAnalysis anly; auto result = parseAnnotationString(propertyDef.value()); @@ -150,6 +157,15 @@ void processField(CXCursor cur, ClassAnalysis* state) { anly.fieldName = fieldName; anly.category = result["category"]; anly.onUpdateCallback = result["on_update"]; + + if (anly.category == "") + anly.category = currentCategory; + + // if name field is not provided, use fieldName instead, but capitalize the first character + if (anly.name == "") { + anly.name = fieldName; + anly.name[0] = std::toupper(anly.name[0]); + } if (result.count("hidden")) anly.flags = anly.flags | PropertyFlags::PropertyFlag_Hidden; @@ -234,6 +250,7 @@ void processClass(CXCursor cur, AnalysisState* state, std::string className, std anly.explorerIcon = result["explorer_icon"]; // Find annotated fields + currentCategory = ""; x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) { CXCursorKind kind = clang_getCursorKind(cur); if (kind != CXCursor_FieldDecl) return CXChildVisit_Continue; diff --git a/core/src/objects/annotation.h b/core/src/objects/annotation.h index 47fa778..39f5f29 100644 --- a/core/src/objects/annotation.h +++ b/core/src/objects/annotation.h @@ -2,26 +2,34 @@ // Markers for the autogen engine to generate getters, setters, lua, etc. +// Base macros #ifdef __AUTOGEN__ #define def_inst(...) clang::annotate("OB::def_inst", #__VA_ARGS__) -#define INSTANCE [[ def_inst() ]] -#define INSTANCE_WITH(...) [[ def_inst(__VA_ARGS__) ]] -#define INSTANCE_SERVICE(...) [[ def_inst(__VA_ARGS__, service) ]] - #define def_prop(...) clang::annotate("OB::def_prop", #__VA_ARGS__) - +#define def_prop_category(...) clang::annotate("OB::def_prop_category", #__VA_ARGS__) #define cframe_position_prop(...) clang::annotate("OB::cframe_position_prop", #__VA_ARGS__) #define cframe_rotation_prop(...) clang::annotate("OB::cframe_rotation_prop", #__VA_ARGS__) #else #define def_inst(...) -#define INSTANCE -#define INSTANCE_WITH(...) -#define INSTANCE_SERVICE(...) #define def_prop(...) +#define def_prop_category(...) #define cframe_position_prop(...) #define cframe_rotation_prop(...) #endif +// Helper macros +#define DEF_INST [[ def_inst() ]] +#define DEF_INST_(...) [[ def_inst(__VA_ARGS__) ]] +#define DEF_INST_ABSTRACT [[ def_inst(abstract) ]] +#define DEF_INST_ABSTRACT_(...) [[ def_inst(__VA_ARGS__, abstract) ]] +#define DEF_INST_SERVICE [[ def_inst(service) ]] +#define DEF_INST_SERVICE_(...) [[ def_inst(__VA_ARGS__, service) ]] +#define DEF_PROP [[ def_prop() ]] +#define DEF_PROP_(...) [[ def_prop(__VA_ARGS__) ]] + +// Categories +#define DEF_PROP_CATEGORY(CATEGORY) [[ def_prop_category(category=CATEGORY) ]] + #define AUTOGEN_PREAMBLE \ protected: \ virtual result InternalGetPropertyMeta(std::string name) override; \ diff --git a/core/src/objects/datamodel.h b/core/src/objects/datamodel.h index 28f82cd..13b68ea 100644 --- a/core/src/objects/datamodel.h +++ b/core/src/objects/datamodel.h @@ -13,7 +13,7 @@ class DataModel; class Service; // The root instance to all objects in the hierarchy -class INSTANCE_WITH(abstract) DataModel : public Instance { +class DEF_INST_(abstract) DataModel : public Instance { AUTOGEN_PREAMBLE private: void DeserializeService(pugi::xml_node node); diff --git a/core/src/objects/handles.h b/core/src/objects/handles.h index d2a5876..a127b0b 100644 --- a/core/src/objects/handles.h +++ b/core/src/objects/handles.h @@ -31,7 +31,7 @@ enum HandlesType { RotateHandles, }; -class INSTANCE_WITH(abstract) Handles : public Instance { +class DEF_INST_(abstract) Handles : public Instance { AUTOGEN_PREAMBLE public: bool nixAxes = false; // XYZ -> ZXY, used with rotation diff --git a/core/src/objects/joint/jointinstance.h b/core/src/objects/joint/jointinstance.h index a4ddf88..4e98876 100644 --- a/core/src/objects/joint/jointinstance.h +++ b/core/src/objects/joint/jointinstance.h @@ -14,7 +14,7 @@ class Part; class Workspace; -class INSTANCE_WITH(abstract) JointInstance : public Instance { +class DEF_INST_ABSTRACT JointInstance : public Instance { AUTOGEN_PREAMBLE std::weak_ptr oldPart0; @@ -31,14 +31,10 @@ protected: virtual void breakJoint() = 0; public: - [[ def_prop(name="Part0", on_update=onUpdated) ]] - std::weak_ptr part0; - [[ def_prop(name="Part1", on_update=onUpdated) ]] - std::weak_ptr part1; - [[ def_prop(name="C0", on_update=onUpdated) ]] - CFrame c0; - [[ def_prop(name="C1", on_update=onUpdated) ]] - CFrame c1; + DEF_PROP_(on_update=onUpdated) std::weak_ptr part0; + DEF_PROP_(on_update=onUpdated) std::weak_ptr part1; + DEF_PROP_(on_update=onUpdated) CFrame c0; + DEF_PROP_(on_update=onUpdated) CFrame c1; JointInstance(const InstanceType*); ~JointInstance(); diff --git a/core/src/objects/joint/rotate.h b/core/src/objects/joint/rotate.h index 0758b7b..37dac97 100644 --- a/core/src/objects/joint/rotate.h +++ b/core/src/objects/joint/rotate.h @@ -5,7 +5,7 @@ #include "objects/joint/jointinstance.h" #include -class INSTANCE Rotate : public JointInstance { +class DEF_INST Rotate : public JointInstance { AUTOGEN_PREAMBLE rp::HingeJoint* joint = nullptr; diff --git a/core/src/objects/joint/snap.h b/core/src/objects/joint/snap.h index 8a6ad63..3f8a327 100644 --- a/core/src/objects/joint/snap.h +++ b/core/src/objects/joint/snap.h @@ -5,7 +5,7 @@ #include "objects/joint/jointinstance.h" #include -class INSTANCE Snap : public JointInstance { +class DEF_INST Snap : public JointInstance { AUTOGEN_PREAMBLE rp::FixedJoint* joint = nullptr; diff --git a/core/src/objects/joint/weld.h b/core/src/objects/joint/weld.h index b9eafdb..1d2f63d 100644 --- a/core/src/objects/joint/weld.h +++ b/core/src/objects/joint/weld.h @@ -5,7 +5,7 @@ #include "objects/joint/jointinstance.h" #include -class INSTANCE Weld : public JointInstance { +class DEF_INST Weld : public JointInstance { AUTOGEN_PREAMBLE rp::FixedJoint* joint = nullptr; diff --git a/core/src/objects/jointsservice.h b/core/src/objects/jointsservice.h index 45db906..43c59ab 100644 --- a/core/src/objects/jointsservice.h +++ b/core/src/objects/jointsservice.h @@ -3,7 +3,7 @@ #include "objects/annotation.h" #include "objects/base/service.h" -class INSTANCE_SERVICE() JointsService : public Service { +class DEF_INST_SERVICE JointsService : public Service { AUTOGEN_PREAMBLE private: std::optional> jointWorkspace(); diff --git a/core/src/objects/part.h b/core/src/objects/part.h index cd7bf3c..e3ebbce 100644 --- a/core/src/objects/part.h +++ b/core/src/objects/part.h @@ -28,7 +28,7 @@ struct PartConstructParams { class Snap; -class INSTANCE_WITH(explorer_icon="part") Part : public Instance { +class DEF_INST_(explorer_icon="part") Part : public Instance { AUTOGEN_PREAMBLE protected: // Joints where this part is Part0 @@ -50,36 +50,33 @@ protected: void OnAncestryChanged(std::optional> child, std::optional> newParent) override; void onUpdated(std::string); public: - [[ def_prop(name="Velocity", on_update=onUpdated) ]] - Vector3 velocity; + DEF_PROP_CATEGORY(DATA) + DEF_PROP_(on_update=onUpdated) Vector3 velocity; [[ def_prop(name="CFrame", on_update=onUpdated), cframe_position_prop(name="Position"), cframe_rotation_prop(name="Rotation") ]] CFrame cframe; - [[ def_prop(name="Size", category=PART, on_update=onUpdated) ]] - glm::vec3 size; - [[ def_prop(name="Color", category=APPEARANCE) ]] - Color3 color; - [[ def_prop(name="Transparency", category=APPEARANCE) ]] - float transparency = 0.f; + + DEF_PROP_CATEGORY(PART) + DEF_PROP_(on_update=onUpdated) glm::vec3 size; + + DEF_PROP_CATEGORY(APPEARANCE) + DEF_PROP Color3 color; + DEF_PROP float transparency = 0.f; bool selected = false; - [[ def_prop(name="Anchored", category=BEHAVIOR, on_update=onUpdated) ]] - bool anchored = false; - [[ def_prop(name="Locked", category=BEHAVIOR) ]] - bool locked = false; - rp::RigidBody* rigidBody = nullptr; + DEF_PROP_CATEGORY(BEHAVIOR) + DEF_PROP bool anchored = false; + DEF_PROP bool locked = false; - [[ def_prop(name="TopSurface", category=SURFACE) ]] - SurfaceType topSurface = SurfaceType::SurfaceStuds; - [[ def_prop(name="BottomSurface", category=SURFACE) ]] - SurfaceType bottomSurface = SurfaceType::SurfaceInlets; - [[ def_prop(name="LeftSurface", category=SURFACE) ]] - SurfaceType leftSurface = SurfaceType::SurfaceSmooth; - [[ def_prop(name="RightSurface", category=SURFACE) ]] - SurfaceType rightSurface = SurfaceType::SurfaceSmooth; - [[ def_prop(name="FrontSurface", category=SURFACE) ]] - SurfaceType frontSurface = SurfaceType::SurfaceSmooth; - [[ def_prop(name="BackSurface", category=SURFACE) ]] - SurfaceType backSurface = SurfaceType::SurfaceSmooth; + DEF_PROP_CATEGORY(SURFACE) + DEF_PROP SurfaceType topSurface = SurfaceType::SurfaceStuds; + DEF_PROP SurfaceType bottomSurface = SurfaceType::SurfaceInlets; + DEF_PROP SurfaceType leftSurface = SurfaceType::SurfaceSmooth; + DEF_PROP SurfaceType rightSurface = SurfaceType::SurfaceSmooth; + DEF_PROP SurfaceType frontSurface = SurfaceType::SurfaceSmooth; + DEF_PROP SurfaceType backSurface = SurfaceType::SurfaceSmooth; + + + rp::RigidBody* rigidBody = nullptr; inline SurfaceType GetSurfaceFromFace(NormalId face) { return surfaceFromFace(face); } diff --git a/core/src/objects/script.h b/core/src/objects/script.h index 249df91..c435568 100644 --- a/core/src/objects/script.h +++ b/core/src/objects/script.h @@ -4,7 +4,7 @@ #include "objects/base/instance.h" #include -class INSTANCE_WITH(explorer_icon="script") Script : public Instance { +class DEF_INST_(explorer_icon="script") Script : public Instance { AUTOGEN_PREAMBLE public: Script(); diff --git a/core/src/objects/script/scriptcontext.h b/core/src/objects/script/scriptcontext.h index 30e0e7a..b197fb5 100644 --- a/core/src/objects/script/scriptcontext.h +++ b/core/src/objects/script/scriptcontext.h @@ -4,7 +4,7 @@ #include "objects/base/service.h" #include "lua.h" -class INSTANCE_SERVICE() ScriptContext : public Service { +class DEF_INST_SERVICE ScriptContext : public Service { AUTOGEN_PREAMBLE protected: void InitService() override; diff --git a/core/src/objects/script/serverscriptservice.h b/core/src/objects/script/serverscriptservice.h index 934bfe5..3caf048 100644 --- a/core/src/objects/script/serverscriptservice.h +++ b/core/src/objects/script/serverscriptservice.h @@ -5,7 +5,7 @@ // Container class for server scripts // Also handles/manages running server scripts on run -class INSTANCE_SERVICE() ServerScriptService : public Service { +class DEF_INST_SERVICE ServerScriptService : public Service { AUTOGEN_PREAMBLE protected: void InitService() override; diff --git a/core/src/objects/workspace.h b/core/src/objects/workspace.h index 548eb04..5d424b4 100644 --- a/core/src/objects/workspace.h +++ b/core/src/objects/workspace.h @@ -31,7 +31,7 @@ class Rotate; typedef std::function)> RaycastFilter; -class INSTANCE_SERVICE(explorer_icon="workspace") Workspace : public Service { +class DEF_INST_SERVICE_(explorer_icon="workspace") Workspace : public Service { AUTOGEN_PREAMBLE rp::PhysicsWorld* physicsWorld = nullptr; diff --git a/docs/autogen.md b/docs/autogen.md index 825381b..ed7a7b1 100644 --- a/docs/autogen.md +++ b/docs/autogen.md @@ -44,10 +44,12 @@ Finally, create a build target that depends on your `AUTOGEN_OUTS`, and make `AL Autogen is an annotation processor, it largely only interacts with C++ annotations (`[[ ... ]]`). It uses libClang to parse through the header and look for classes annotated with `OB::def_inst`. In `annotation.h`, there are various aliases for this command: -- `INSTANCE` - Simply annotate with `def_inst`, do not do anything more. Equivalent to `[[ def_inst() ]]` -- `INSTANCE_WITH(...)` - Same as above, but allows you to pass in arguments to `def_inst`. Equivalent to `[[ def_inst(...) ]]` -- `INSTANCE_SERVICE(...)` - Same as above, but adds `, service` to the end of the argument list, marking the instance as a service. -Equivalent to `[[ def_inst(..., service) ]]` +- `DEF_INST` - Simply annotate with `def_inst`, do not do anything more. Equivalent to `[[ def_inst() ]]` +- `DEF_INST_(...)` - Same as above, but allows you to pass in arguments to `def_inst`. Equivalent to `[[ def_inst(...) ]]` +- `DEF_INST_SERVICE` - Annotates with `def_inst`, but passes `service` as an argument, marking the instance as a service. +- `DEF_INST_SERVICE_(...)` - Same as above, but lets you add additional arguments +- `DEF_INST_ABSTRACT - Annotates with `def_isnt`, but passes `abstract` +- `DEF_INST_ABSTRACT_(...) - Same as above, but let's you pass additional arguments Then, there is the command itself: @@ -71,7 +73,7 @@ Properties are also a key component. Inside every class annotated with `def_inst Here are its parameters: -- `name` - Required parameter, this is the name of the property itself. The name of the field is not taken into consideration (subject to change) +- `name` - Option, name of the property. If this is not specified, it will use the field's name, with the first letter capitalized - `hidden` - Flag, marks the property as hidden from the editor. - `no_save` - Flag, the property should not be deserialized nor serialized - `readonly` - Flag, the property cannot be assigned to @@ -80,6 +82,11 @@ Here are its parameters: The type of the property, and conversion to and from it and the datatype system is automatically inferred. `std::string` is interpreted as `Data::String`, and `std::weak_ptr` is also converted to/from `Data::InstanceRef`. In the future, if weird edge-case types are introduced, the code generator may need to be extended. See [Extending Autogen](#extending-autogen) +Similarly to `def_inst`, `def_prop` also has its aliases: + +- `DEF_PROP` - Annotates the field with `def_prop`. Equivalent to `[[ def_prop() ]]` +- `DEF_PROP_(...)` - Same as above, but allows you to pass in arguments. Equivalent to `[[ def_prop(...) ]]` + In Part, it is necessary to expose the position and rotation components of the CFrame as properties, so there are two commands for this case (these should be added alongside `def_prop` on the CFrame property): cframe_position_prop(name=) @@ -87,26 +94,50 @@ In Part, it is necessary to expose the position and rotation components of the C They simply create properties of the component with the specified name. They will inherit the category and update callback from the CFrame property, and will be flagged with NOSAVE. A getter/setter is automatically generated which generates a new CFrame with the specific component changed for you. +Setting the category of each can be cumbersome and annoying. Rather than setting it on each field, you can use `DEF_PROP_CATEGORY` to apply the specified +category to the current field and all that follow it. + +Sometimes, you will need to reference another object in your header. For instance, if you use a specific object type for a `weak_ptr` (e.g. +`std::weak_ptr`). Doing so with an `#include` will unnecessarily add a dependency into the build chain for that translation unit. Instead, you may +opt to use a forward-declaration before your class declaration to avoid this overhead. + +However, Autogen cannot tell where this class came from to automatically include its header in the generated file. So, you must include its header in an `#ifdef` +block via `__AUTOGEN_EXTRA_INCLUDES__`: + + #ifdef __AUTOGEN_EXTRA_INCLUDES__ + #include "objects/part.h" + #endif + + class Part; + + class DEF_INST SomeObject { + AUTOGEN_PREAMBLE + public: + DEF_PROP std::weak_ptr myPart; + } + +Autogen will automatically define `__AUTOGEN_EXTRA_INCLDUES__` before including `SomeObject`'s header file, so the `#ifdef` block will evaluate, pulling in +the necessary dependencies + ### Example Here is an example of an instance annotated using Autogen - class INSTANCE Part { + class DEF_INST Part { AUTOGEN_PREAMBLE public: - [[ def_prop(name="Color") ]] - Color3 color; + DEF_PROP_CATEGORY("APPEARANCE") + DEF_PROP Color3 color; + DEF_PROP CFrame cframe; - [[ def_prop(name="CFrame"), cframe_position_prop(name="Position") ]] - CFrame cframe; - - [[ def_prop(name="ReadOnly", readonly) ]] + DEF_PROP_CATEGORY("DATA") + DEF_PROP_(name="ReadOnly", readonly) int readOnlyValue; - [[ def_prop(name="Ephemeral", no_save) ]] + DEF_PROP_(no_save) std::string ephemeral; - [[ def_prop(name="SuperSecretValue", no_save, hidden) ]] + DEF_PROP_(name="SuperSecretValue", no_save, hidden) std::string superSecret; };