{"id":25675553,"url":"https://github.com/notrabs/rrcg","last_synced_at":"2025-08-02T07:41:41.844Z","repository":{"id":192451261,"uuid":"684742196","full_name":"notrabs/RRCG","owner":"notrabs","description":"C# to CV2 Graph Compiler","archived":false,"fork":false,"pushed_at":"2024-11-26T19:16:30.000Z","size":3833,"stargazers_count":9,"open_issues_count":1,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-23T08:09:05.098Z","etag":null,"topics":["compiler","cv2","recroom","unity","visualscripting"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-2.1","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/notrabs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-08-29T19:00:57.000Z","updated_at":"2025-03-14T17:11:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"a3de4aae-491d-4036-a3ab-7f6ea02eb5f8","html_url":"https://github.com/notrabs/RRCG","commit_stats":null,"previous_names":["notrabs/rrcg"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/notrabs/RRCG","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notrabs%2FRRCG","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notrabs%2FRRCG/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notrabs%2FRRCG/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notrabs%2FRRCG/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/notrabs","download_url":"https://codeload.github.com/notrabs/RRCG/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notrabs%2FRRCG/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268348786,"owners_count":24236306,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compiler","cv2","recroom","unity","visualscripting"],"created_at":"2025-02-24T13:17:13.849Z","updated_at":"2025-08-02T07:41:41.784Z","avatar_url":"https://github.com/notrabs.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RRCG - Rec Room Circuit Generator\n\nWhat if you never had to move a wire by hand? RRCG brings text-based scripting support to [Rec Room's](https://recroom.com/) visual scripting language CV2.\n\n![example-circuit](./Docs/Images/header.png)\n\n\n| :warning: WARNING |\n| --- |\n| **Consider upvoting this feature request**: **https://recroom.featureupvote.com/suggestions/482338/circuits-api** \u003cbr /\u003e This package only contains the compiler frontend to validate the graph generation. Wihout a proper Circuits API the conversion into actual graphs is to unstable to release. |\n\n\u003c!-- toc --\u003e\n\n1. [Install](#install)\n2. [Using the Compiler](#using-the-compiler)\n3. [Writing Code](#writing-code)\n4. [Troubleshooting](Docs/Troubleshooting.md)\n5. [Feature Matrix](Docs/FeatureMatrix.md)\n\n## Install\n\nRRCG comes as a Unity package to be installed in your [Rec Room Studio](https://docs.alexagirl.studio/docs) project.\n\n[Using the Package Manager](https://docs.unity3d.com/Manual/upm-ui-giturl.html) install a package from this Git URL:\n`https://github.com/notrabs/RRCG.git`\n\n\u003cdetails\u003e\n\u003csummary\u003e How to Update \u003c/summary\u003e\n\nOccasionally an update might include breaking changes, most likely if chips get changed or the compiler internals are changed. This can invalidate RRCG's generated files. To safely update, please follow these steps:\n\n1. **Make sure Unity is open before you update**\n2. Update using the Package Manager (or git)\n3. If you get errors in generated files =\u003e Use \"Clean All\" in the window menu\n4. If you get errors in source files =\u003e Resolve them manually (e.g. adjust to chip changes)\n5. Hit \"Recompile All\" to make sure your files are compiled with the latest compiler\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e For development \u003c/summary\u003e\n\nClone the repository into the \"Packages\" folder of your Studio project.\n\ne.g. as a submodule: `git submodule add https://github.com/notrabs/RRCG.git Packages/RRCG`\n\n\u003c/details\u003e\n\n## Using the Compiler\n\n1. Create a prefab from the `RRCG` window menu. Place it in a location with enough space. The chip area will grow as indicated by the arrows.\n2. Open the Inspector for the `RRCG` prefab\n3. Select a `CircuitDescriptor` (or use the [example](https://github.com/notrabs/RRCG/blob/main/Example/ExampleRoom.rrcg.cs))\n4. Click `Build Circuit` (placeholder for now. Until we have a Circuits API you can only create the debug DOT Graph)\n\n\u003cdetails\u003e\n\u003csummary\u003e RRCG script files \u003c/summary\u003e\n\nRRCG compiles every file in your project with a `.rrcg.cs` extension. Any `CircuitDescriptor` class that was successfully compiled by RRCG will be available to select in the RRCG inspector. See the next chapter for how to write valid code.\n\nYou can get started with this [example file](https://github.com/notrabs/RRCG/blob/main/Example/ExampleRoom.rrcg.cs) that is configured by default when you spawn the prefab.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e Standalone Projects \u003c/summary\u003e\n\nRRCG offers an advanced standalone compiler to speed up your iteration cycles. It bypasses Unity's dreadingly slow Assembly Reload, without sacrificing any major benefits that come from being integrated into Unity. The main difference in development is where the script files are located. Instead of in the `Assets` folder, your circuit scripts are placed inside an `RRCG` folder in the project root. The syntax and your access to Unity data stays the same.\n\nTechnically this is achieved by dynamically compiling and loading each iteration of your code. This works for RRCG (and not regular Unity Editor code), because RRCG code contains no side-effects beyond the compilation process.\n\nThe main limitation for now is that there is no mechanism for dependencies between standalone projects yet, but you can still reference any assembly compiled within your regular Assets. So the compromise for now is that shared RRCG code needs to stay within the slow Unity compiler.\n\nTo get started using standalone projects:\n\n1. Open the Inspector for your `RRCG` prefab\n2. Select the \"Standalone Project\" option\n3. Create a new project (or select an existing one)\n4. Wait for the initial compilation\n5. Select a Descriptor from the project (the project generates with a standard one)\n6. Open the RRCG_Project.sln in the `RRCG` folder of your Project root\n\nAfter that, you can build circuits like with the integrated workflow, just without waiting. Subsequent builds within a session should also see additional speed ups thanks to caching mechanisms.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e Watch Mode \u003c/summary\u003e\n\nWith watch mode on all `.rrcg.cs` files in your project are compiled automatically when Unity imports them. This also happens every time you make a change to a script file. There's no downside to leaving it watch mode on, but the option to disable it is there if you want to disable automatic compilation during development.\n\nIn case you want to manually recompile a file you can use Unity's reimport functionality or use the \"Recompile all\" feature from the `RRCG` window menu. This should only be needed after you downloaded a new compiler version or during compiler development.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e DOT Graph \u003c/summary\u003e\n\nDOT is a standard graph format that can be [visualized online](https://dreampuf.github.io/GraphvizOnline/). You can copy a DOT graph for a compiled circuit by pressing the button in the inspector.\n\n\u003c/details\u003e\n\n## Writing Code\n\nThe goal of this language is to be an intuitive, direct mapping of C# to Circuits. With the C# execution flow being mapped to exec lines and data flow being mapped to data lines.\nC# Language features should do what you expect.\n\n### The Circuit Descriptor\n\nThe Circuit Descriptor is your entry point. Your chips start building from the `CircuitGraph()` method, but beyond that you can organize your code however you like. Place it inside a `.rrcg.cs` file anywhere in your project.\n\n```c#\nusing RRCGSource;\n\npublic class ExampleRoom : CircuitDescriptor\n{\n    public override void CircuitGraph()\n    {\n        // Your circuits go here\n    }\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e Circuit Libraries \u003c/summary\u003e\n\nIf you want to create reusable logic without an entry point, extend the `CircuitLibrary` class instead.\nThis will also hide the class in the Circuit Selection menu. Place it inside a `.rrcg.cs` file anywhere in your project.\n\n```c#\nusing RRCGSource;\n\npublic class ExampleLibrary : CircuitLibrary\n{\n    // Your circuits go here.\n    // You can use it as a normal class or with static methods.\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e Additional Entry Points \u003c/summary\u003e\n\nA room is usually made up of multiple graphs.\nYou can create separate graphs within a function by using exec chips with no exec inputs, or the `StartNewGraph()` method.\nBut for code organization it is often nicer to have them as separate methods.\nUse the `[CircuitGraph]` attribute to mark functions in your CircuitDescriptor as additional entry points.\nNote that they must be parameterless functions to work.\n\n```c#\npublic class ExampleRoom : CircuitDescriptor\n{\n    public override void CircuitGraph()\n    {\n        // Your 1st graph goes here\n\n        EventReceiver(); // implicitly creates a new graph, because receivers have no exec input.\n\n        // Your 2nd graph goes here\n\n        StartNewGraph(); // explicitly creates a new graph.\n\n        // Your 3rd graph goes here\n    }\n\n    [CircuitGraph]\n    void Foo()\n    {\n        // Your 4th graph goes here\n    }\n}\n```\n\nSometimes you need an exec chip, without connecting it to your exisiting execution flow.\nYou could place it in a separate graph and reference to it with variables, but that quickly gets very verbose.\nInstead use the `InlineGraph()` helper to create new graphs without destryoing your current context.\n\n```c#\npublic class ExampleRoom : CircuitDescriptor\n{\n    EventDefinition\u003cstring\u003e onInput = new EventDefinition\u003cstring\u003e();\n\n    public override void CircuitGraph()\n    {\n        ChipLib.Log(\"Start\");\n\n        // Placing a receiver would normally create a new graph. This only extracts its data.\n        var input = InlineGraph(() =\u003e onInput.Receiver());\n\n        // This log chip will be connected to the previous log chip, but get the data from the event receiver.\n        ChipLib.Log(input);\n    }\n}\n```\n\n\u003c/details\u003e\n\n### Placing Chips\n\nChips are available as static functions in the `Chips` class. For convenience you can access them through the extended `CircuitDescriptor` class.\n\n```c#\npublic void ExampleCircuit()\n{\n    Chips.RandomInt(1,10);\n    // or\n    RandomInt(1,10);\n}\n```\n\nIn addition to placing chips explicitly, you can also use the shorthands provided in the classes of RR types.\n\n```c#\npublic void ExampleCircuit()\n{\n    SetPosition(GetLocalPlayer(), new Vector3(1,2,3));\n    // or\n    GetLocalPlayer().Position = new Vector3(1,2,3);\n}\n```\n\n### Data flow\n\nPorts are data. Data is Ports. Don't worry what the type system might say. Write code as you usually would. RRCG will create and connect chips when necessary to execute operations.\n\n```c#\npublic void ExampleCircuit()\n{\n    int rand1 = RandomInt(0, 10);\n    var rand2 = RandomInt(0, rand1);\n\n    if (rand1 + rand2 \u003e 10) LogString(\"Today's your lucky day\");\n    else LogString(\"Try again next time\");\n}\n```\n\nIf a chip has multiple outputs, a tuple will be returned\n\n```c#\npublic void ExampleCircuit()\n{\n    // The underscore is handy to discard unwanted values\n    var (parsed, _) = ParseFloat(\"1.0\");\n\n    // You can also access the named fields of the tuple to quickly get single values\n    var value = ParseFloat(\"1.0\").Parsed;\n}\n```\n\n### Exec flow\n\nFunctions are invisible. By default the execution flow follows the first pin. If a Chip has no exec input, it automatically starts a new graph.\n\n```c#\npublic void ExampleCircuit()\n{\n    // starts a new graph\n    RoomEvent.Hz30();\n\n    // connects the random chip inside the function directly to the event receiver\n    var rand1 = MyFunction();\n    LogString(ToString(rand1));\n\n    // starts a new graph\n    RoomEvent.Hz30();\n    LogString(\"1\");\n}\n\npublic void MyFunction()\n{\n    return RandomInt(1, 2);\n}\n```\n\n\"If\" and Execution Switches are fully supported. A `throw` statement ends a branch of execution.\n\n```c#\npublic void ExampleCircuit()\n{\n    var num = Random(1,10);\n\n    if (num == 5) {\n      LogString(\"high five\");\n      throw null;\n    } else {\n      LogString(\"else and else if are supported\");\n    }\n\n    switch (num){\n      case 1:\n        LogString(\"1\");\n        break;\n      case 2:\n        LogString(\"2\");\n        break;\n      default:\n        LogString(\"There's gotta be a more efficient way\");\n        break;\n    }\n}\n```\n\n(returns are in the works)\n\n### Operator Overload\n\nMath Operators and comparisons will create the according chips, and evaluate using c#'s order of operations.\n\n```c#\npublic void ExampleCircuit()\n{\n    var result = RandomInt(1,5) + 1 - 2 * 3 / 4 % 5;\n    if (result \u003e 0 \u0026\u0026 result \u003c 0) {\n        LogString(\"This is a scientfic breakthrough\");\n    }\n}\n```\n\n### Automatic Casting\n\nThe `FromRecRoomObject` chip has been hidden since it doesn't map nicely into c# types. Instead you can use implicit casts to convert objects:\n\n```c#\npublic void ExampleCircuit()\n{\n    var text = RecRoomObjectGetFirstwithTag(\"text\");\n    // Using text here will automatically insert a FromRecRoomObject chip\n    TextSetText(text, \"Hello\");\n}\n```\n\n### Circuit Boards\n\nYou can generate code for existing circuit boards (or control panels). Their interface needs to be already defined in the game:\n\n```c#\npublic void ExampleCircuit()\n{\n    // ... other circuits placed directly in the room\n\n    ExistingCircuitBoard(\"Board Name\", CircuitBoard);\n}\n\npublic void CircuitBoard()\n{\n    ExistingExecInput(\"AddRandom\");\n    var sum = ExsitingDataInput\u003cint\u003e(\"number\") + RandomInt(1,10);\n    ExistingDataOutput(\"Sum\", sum);\n    ExistingExecOutput(\"AddRandom\");\n}\n```\n\nYou can create new Circuit Boards from a function. An exec port will be automatically added when executable nodes are inside. The inputs/outputs match the function interface. If you want to return multiple ports, make sure to wrap them in a tuple.\n\n```c#\npublic void ExampleRoom()\n{\n    // This board will only have data pins\n    var (a, b) = CircuitBoard(MathBoard, 5, -5);\n\n    // This board will have execution pins\n    CircuitBoard(RandomBoard, a, b);\n}\n\n// the output ports will be named after parameter names and names in the tuple return\npublic (int sum, int difference) MathBoard(int a, int b)\n{\n   return (a + b, a - b);\n}\n\npublic int RandomBoard(int a, int b)\n{\n   return RandomInt(a, b);\n}\n```\n\n### Chip Lib\n\nThe Chip Lib contains useful helpers to write common patterns. Check back as more are implemented:\n\n```c#\npublic void ExampleCircuit()\n{\n    // The EventCache creates a simple event to cache data for later execution ticks\n    var expensiveSum = Add(3,4);\n    var cachedSum = ChipLib.EventCache\u003cint\u003e(expensiveSum);\n\n    // Log() automatically converts any value ToString\n    ChipLib.Log(cachedSum);\n\n    // AwaitDelay() continues on the delayed line of a Delay chip. Useful if you don't need immediate logic.\n    ChipLib.AwaitDelay();\n    ChipLib.AwaitDelay(1);\n}\n```\n\n### Event Helpers\n\nThe `EventDefintion` class helps you to write type-safe code. You can define the structure of an event once, and then place all event chips with correct typings:\n\n```c#\n// This will create a new event definition named \"onInput\"\nEventDefintion\u003cint\u003e onInput = new EventDefintion\u003cint\u003e(\"value\");\n\npublic void ExampleCircuit()\n{\n    // Start new circuit graphs at the receiver\n    var data = onInput.Receiver();\n\n    // Send Events using one of the sender functions\n    onInput.SendLocal(123);\n}\n```\n\nAccess the predefined events using the methods in the `RoomEvent` class:\n\n```c#\npublic void GameLoop()\n{\n    var deltaTime = RoomEvent.Hz30();\n    // ... react to event\n}\n```\n\nAccess your existing events with the `ExistingEvent` function:\n\n```c#\n// This assumes an Event definition with name \"EventName\" and one int value is already placed in the room\nEventDefintion\u003cint\u003e onInput = ExistingEvent\u003cint\u003e(\"EventName\");\n```\n\nStudio Events are referenced by name. There is a small helper to make this more readable, but a named event receiver also works:\n\n```c#\npublic void StudioBoard()\n{\n    StudioEventReceiver(\"StudioEventName\");\n    // ... react to event\n}\n```\n\nUse the `[EventFunction]` Attribute to automatically convert a function call into event senders, with all the logic only being placed once in the world. You can return a value like in other functions, where it will be shared by all users.\n\n```c#\npublic void ExampleCircuit()\n{\n    // will be placed as two event senders\n    ExpensiveFunction(\"World\");\n    ExpensiveFunction(\"World 2\");\n}\n\n[EventFunction]\npublic void ExpensiveFunction(string parameter)\n{\n    // only one LogString will be placed in the world, and retrieve the parameter from the event\n    LogString(\"Hello\" + parameter);\n}\n```\n\n### Variable Helpers\n\nThe `Variable`, `SyncedVariable` and `CloudVariable` classes help you to write type-safe code. Instance and Synced Variables are named automatically and uniquely for each instance.\n\n```c#\nVariable\u003cint\u003e count = new Variable\u003cint\u003e();\nSyncedVariable\u003cint\u003e syncedVarWithHomeValue = new SyncedVariable\u003cint\u003e(2);\nCloudVariable\u003cstring\u003e cloudVar = new CloudVariable\u003cstring\u003e(\"Name_of_my_Variable\");\n\npublic void ExampleCircuit()\n{\n    // Places a configured event receiver\n    count.ChangedEvent();\n\n    // Access/Modify the Value using the Value getter/setter\n    count.Value = count.Value + 1;\n}\n```\n\nUsing attributes, this code can be made less verbose for member variables. This code without `.Value` accesses is equivalent to the code above:\n\n```c#\n[Variable]\nint count;\n\n[SyncedVariable]\nint syncedVarWithHomeValue = 2;\n\n[CloudVariable(\"Name_of_my_Variable\")]\nstring cloudVar;\n\npublic void ExampleCircuit()\n{\n    // The compiler will parse the changed event out of the reference to the C# value.\n    MemberVariableChanged(count);\n\n    // Access/Modify the Value using the c# variable\n    count++;\n}\n```\n\n### Conditional assignments\n\nThe compiler will automatically handle the creation of temporary variables that are required to implement conditional assignments.\n\n```c#\npublic int Example(bool condition){\n    int a = 1; // This will remain optimized value that can be inserted into ports\n    int b = 2; // This will be created as a variable, due to the conditional assignment below.\n\n    if (condition) b = -2;\n\n    return a + b;\n}\n```\nThis also works for loops, so you can create proper iteration variables without using the Variable classes explicitly:\n```c#\npublic void Example(){\n    int i = 0;\n\n    while (i \u003c 10) {\n        ChipLib.Log(i);\n\n        i++;\n    }\n}\n```\n\n### Shared Properties\n\nUse the `[SharedProperty]` Attribute to annotate functions that should only be placed once. All calls to this function will connect to the same graph.\nThis is a bit of syntactic sugar to work around c# not allowing you to access VariableHelpers during initialization. If possible you should place your shared chips into the class body.\n\n:warning: It is up to the user to ensure that SharedProperty functions are pure and do not contain any execs. You can pass parameters into the function, but they will be only connected during the first call of the function.\n\n```c#\n// Simple static chips can also be shared by storing them directly in the class.\nint SharedTimeChip = TimeGetUniversalSeconds();\n\nVariableHelper roundStarted = new VariableHelper\u003cint\u003e();\n\npublic void ExampleCircuit()\n{\n    ChipLib.Log(RoundTime());\n    ChipLib.Log(RoundTime());\n}\n\n[SharedProperty]\npublic int GameTime()\n{\n    // only placed once in the world\n    return SharedTimeChip - roundStarted.Value;\n}\n```\n\n### Interfacing with Studio Objects\n\nRRCG provides a built-in mechanism to interface with Studio Objects. The goal is to eventually allow you to create all logic within C#. For this your Studio Object needs to be described by a `StudioObjectDescriptor` class in a `.rrcg.cs` file.\n\n```c#\nusing RRCGSource;\n\npublic class MyStudioObject : StudioObjectDescriptor\n{\n    // Copy this constructor and change the prefab name to match your prefab.\n    public MyStudioObject(StudioObject target) : base(target, \"StudioObjectPrefabName\") { }\n\n    // The [ExistingStudioFunction] allows you to call manually defined Studio Functions.\n    // Make sure their interface and name match to the function defined!\n    [ExistingStudioFunction]\n    public void SetScale(Vector3 scale) { }\n\n    // For manually defined functions return \"default\". The function will work once in-game.\n    [ExistingStudioFunction]\n    public Vector3 GetPosition() { return default; }\n\n    // If your function has multiple outputs, return a tuple\n    [ExistingStudioFunction]\n    public (Vector3, Quaternion) GetPositionAndRotation() { return default; }\n\n    // Coming soon: A [StudioFunction] attribute to generate Studio Functions automatically from C# code!\n}\n\n```\n\nTo use a function from a CircuitDescriptor, create an instance of your StudioObjectDescriptor class and call its functions directly.\n\n```c#\npublic class ExampleRoom : CircuitDescriptor\n{\n    public override void CircuitGraph()\n    {\n        var rro = RecRoomObjectGetFirstWithTag(\"mystudioobj\");\n\n        // Pass a reference to your studio object. It will be connected to the according event/function pins.\n        var myStudioObject = new MyStudioObject(rro);\n\n        // Directly call the functions defined in your StudioObjectDescriptor\n        myStudioObject.SetScale(new Vector3(1,2,3));\n        var position = myStudioObject.GetPosition();\n    }\n}\n```\n\n### Interfacing with Unity\n\nYou can directly call functions in other Editor scripts or libraries. They will be evaluated when you build the circuits.\n\n:warning: Make sure the data passed to outside functions is valid c# data. If it holds a chips' port output, the function needs to support the according `Port` type. Take a look at the \"Custom Building Code\" chapter to learn more about the compilation process.\n\n```c#\npublic void ExampleCircuit()\n{\n    var numOfGameObjects = GameObject.Find(\"Ball\").Count();\n    LogString(\"Number Of GameObjects:\");\n    LogString(ToString(numOfGameObjects));\n}\n```\n\n## Custom Building Code (.rrcg.gen.cs files)\n\nFor more advanced use-cases, the C# source code translation might not be expressive enough for your needs.\nExpecially the conversion of control structures is limiting, if you want to create dynamic circuits.\n\nOne simple example for dynamic chip generation is the `ChipLib.Log(object)` helper.\nIt needs to insert a ToString chip, if and only if the inputted data is not already a string.\n\nIf you look at the ChipLib implementation you'll notice that the source-realm (=`RRCGSource` namespace) function is empty:\n\n```c#\nnamespace RRCGSource {\n    /// \u003csummary\u003e\n    /// Logs a value to the console with automatic ToString conversion\n    /// \u003c/summary\u003e\n    public static void Log(object obj) { }\n}\n```\n\nUnlike normally compiled code, the ChipLib has been manually translated into the build-realm (=`RRCGBuild` namespace) to take advantage of customized building code.\n\n```c#\nnamespace RRCGBuild {\n    public static void Log(AnyPort obj)\n    {\n        StringPort stringPort;\n        if (obj is StringPort sp) stringPort = sp;\n        else stringPort = obj.ToString();\n\n        LogString(stringPort);\n    }\n}\n```\n\nFor normal RRCG scripts this conversion happens automatically with the compiler.\n\n#### Compilation Pipeline\n\nTo understand how the build realm fits into the compilation process, it helps to have a rough idea on how RRCG is implemented.\n\n![image](./Docs/Images/flow_chart.png)\n\nThe conversion of RRCG scripts into CV2 circuits does not happen in a single step.\nThe source-realm code you write isn't executable.\nThe actual circuit graph building happens when the build realm code (=`rrcg.gen.cs` files) is executed.\nMost of the magic of RRCG happens in the first syntax transformation that converts your code into executable build-realm automatically.\n\nIt is during the execution of the build-realm code that we can also execute custom building code to create chips with standard C# logic. Since we are not limited by the syntax of a small C# subset, we can have more control at the price of a bit more verbose syntax.\n\n#### Writing Build Realm code\n\nBuild-realm code is roughly structured like normal RRCG scripts.\nYou can look at the generated files to get an idea, but the main concept to understand is that calling a circuit function will spawn that node into the current graph.\nThe functions get access to the current graph via the static `Context.current` property, so the timepoint of execution dicatates where chips are placed in the execution flow.\nAt the same time also only executed functions have their chips placed.\n\nFor example, this means that unlike in the source-realm, this function will only spawn one chip.\n\n```c#\nnamespace RRCGBuild {\n    void BuildFunction(){\n        // This if is an actual if in the build-realm. Executing it will evaluate\n        // the bool as \"true\", and go into the if branch.\n        if (true) {\n            LogString(\"Chip A\");\n        } else {\n            // This chip will not spawn, because the code is not reachable\n            LogString(\"Chip B\");\n        }\n    }\n}\n```\n\nHaving full c# capabilities back in turn means, that there is also no magical interchangeability between C# data and CV2 data anymore.\nYou need to be explicit about what is CV2 data and what is a regular C# type.\nRRCG uses the `[...}Port` classes to distinguish one from the other.\nThe CV2 type can be obtained by appending \"Port\" to the original type name:\n\n- `bool` =\u003e `BoolPort`\n- `string` =\u003e `StringPort`\n- `Player` =\u003e `PlayerPort`\n- ...\n\nThese Port classes are still fairly clever as they implement a lot of the logic that you already know from the source-realm.\nFor example doing arithmetic on `FloatPorts` will still only create math chips as needed.\nOnly once the Port is an actual port that belongs to a chip a connection will be made when it is used.\n\n```c#\nvoid BuildFunction(){\n    FloatPort valA = 1;\n    FloatPort valB = 5;\n    FloatPort abs = AbsoluteValue(valA - valB);\n}\n```\n\n#### Get started\n\nTo use the build realm simply write your code in a non-compiled file and provide an interface to it in both, the `RRCGSource` and `RRCGBuild` namespaces.\nMore documnentation to come, but looking at the ChipLib source would be a good place to start looking for inspiration.\n\n## Roadmap\n\nThings to do that are in scope of the RRCG project. Although contributions are welcome, even if not listed here.\n\n- [ ] Circuit Building Backend (with an official API)\n- [ ] Support more CV2 features\n- [ ] Support more C# language features\n- [ ] Compiler improvements\n- [ ] Circuit Formatter improvements\n- [ ] Decompilation (Circuits to Code)\n- [ ] Circuit Graph Optimization\n- [ ] Online playground ([SharpLab](https://github.com/ashmind/SharpLab) looks prmomising)\n\n## Useful Resources\n\n- [Roslyn Quoter](https://roslynquoter.azurewebsites.net/)\n- [DOT Graph Visualizer](https://dreampuf.github.io/GraphvizOnline/)\n- [CV2 chip definitions](https://github.com/tyleo-rec/CircuitsV2Resources/blob/master/misc/circuitsv2.json)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotrabs%2Frrcg","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnotrabs%2Frrcg","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotrabs%2Frrcg/lists"}