C# Cloud module - Can i customize binding generation?

Im migrating from a older cloud code version - where we did not have the “Generate Code Bindings” Option, so i had to rely on DTO dll + some automation to get it on project, and it worked no problem!

With the new solution, only whatever is directly referenced in my return/request seems to be created, the problem is that my solution relies on a polymorphic list - i add all the data for my services into a single list of “service data”. As the solution only directly uses “service data” interface and not its implementations, the objects do not get created :frowning:

Is there a easy way for me to flag a object and force the creation? a attribute perhaps?

I was looking into changing the source code of the data generation but at that point im better of using my old solution together with the deployment API

1 Like

Not currently, no, I have an ugly workaround until I brainstorm this a bit with the team, since it means traversing the class hierarchy upwards within the bounds of only user-libraries, it could be tricky :thinking: .

I recommend creating an endpoint with the concrete types, this will be equivalent to this “force” you’re mentioning.

I think the attribute would make a lot of sense, I think we have something similar to that in the backlog. (sorry for delayed response, coming back from holidays).

Yeah, the attribute would be super cool - but i manage to create a workaround that is pretty much everything i needed

  • I manually created a DTO project as described in the Advanced topics of documentation
  • On deploy/build a DLL is generated for the DTO
  • On deploy, i have a callback using the new API to copy the file and move to project

I cant really use the concrete endpoint, because my initialization module - as a example - is a polymorphic list so i can batch all my entry requests

Code
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Services.CloudCode.Core;
using DTO.GameModules;

public class GameModulesController
{
    private ILogger<GameModulesController> logger;
    private PlayerData playerData;
    private GameState gameState;
    private GameConfig gameConfig;

    public GameModulesController(ILogger<GameModulesController> logger, PlayerData playerData, GameState gameState, GameConfig gameConfig)
    {
        this.logger = logger;
        this.playerData = playerData;
        this.gameState = gameState;
        this.gameConfig = gameConfig;
    }

    [CloudCodeFunction("InitializeModules")]
    public async Task<GameData> Initialize(IExecutionContext context, IEnumerable<IGameModule> modules)
    {
        GameData gameData = new GameData();
        foreach (var module in modules)
        {
            try
            {
                IGameModuleData moduleData = await module.InitializeModule(context, playerData, gameState, gameConfig);
                gameData.AddModuleData(moduleData);
            }
            catch (Exception e)
            {
                logger.LogError(e, e.Message);
                throw new Exception($"Something wrong with module {module.GetType().Name}");
            }
        }
        return gameData;

    }
}

DTO
namespace DTO.GameModules
{
    public class GameData
    {
        public GameData()
        {
            lookup = new Lazy<Dictionary<Type, IGameModuleData>>(() => dataList.ToDictionary(data => data.GetType(), data => data));
        }

        private Lazy<Dictionary<Type, IGameModuleData>> lookup;

        [JsonProperty(NullValueHandling = NullValueHandling.Ignore, TypeNameHandling = TypeNameHandling.Auto, ItemTypeNameHandling = TypeNameHandling.Auto)]
        private List<IGameModuleData> dataList = new List<IGameModuleData>();

        public void AddModuleData(IGameModuleData data)
        {
            dataList.Add(data);
        }

        public T GetModuleData<T>() where T : IGameModuleData
        {
            return (T)lookup.Value[typeof(T)];
        }
    }
}

WIth this, i dont really need unity generate bindings - as i use the DLL for any DTO, and my “mid-game” requests all use a “request”-type also generated here.

The bindings for the -CALL- itself are also pretty useless in my experience, as normally you want your own middle-man to make the calls so you can control it better…so i ended up never using them in any project.

It did require me to add one extra step on project setup - the DTO solution - and create a custom window for triggering the upload/build, but nothing out of this world

Ok, thx for the update

and create a custom window for triggering the upload/build
why do u need this?

Can I recommend just linking the cs files from Unity unto your DTO project (you dont even need a project you can link them straight into the main project like so:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <ImplicitUsings>disable</ImplicitUsings>
        <OutputType>Library</OutputType>
    </PropertyGroup>

    <!-- This part here will allow you to include cs files that are not in the project -->
    <ItemGroup>
        <Compile Include="../../Assets/DTOs/**/*.cs" />
    </ItemGroup>

</Project>

Reason for having a custom window was mostly due to automations (single button to upload all my backend - cloud code, remote config etc), Mostly because i couldnt figure out a way to hook-up to the Deployment API.

YOU NEED to have the deployment window open to hook-up the events…and its not considered “open” even if its…actually open. something has to manually call the Open method for it to allow you to register events…

So either i would:

  • Click to open deployment window
  • Click to register my callbacks
  • Click to deploy

or create a single custom deploy window, that i just have to click deploy.

Now - using the unity files inside can kind of remove that need, i can have all payloads and DTOS linked as you suggested BUT

  • I like to decouple the work, and make it as independent as possible and as human-error-safe as possible.

  • I have to investigate how much work and how much flexibility i would have into maintaining the ItemGroup list and the referenced file. I like to work from a minimum maintanence perspective, so if i have to tell someone from my team that he has to update this itemGroup list whenever he does X i tend to avoid it

Hi, just to clarify my understanding what’s the difference between that and just using the deployment window + deploy all? Are there extra steps you are taking before or after deployment?

and its not considered “open” even if its…actually open
Click to register my callbacks

This is weird, if you have repro steps we could take a look, I will try to repro as-is.

I have to investigate how much work and how much flexibility i would have into maintaining the ItemGroup list and the referenced file

The itemlist is a wildcard *.cs. As long as the DTOs are in the same directory it will just work.

My understanding right now is that you’re copying over a dll from your CloudCode solution to the assets folder, correct?

If so that’s more maintenance and trickier since it’s harder to guarantee the classes built on > .net 6 will work with the editor, but the opposite is easier. I’d be happy to try and understand better your use case and help out more, if you want to spend the time of course, as you are probably not the only one with the use-case.

Hi, just to clarify my understanding what’s the difference between that and just using the deployment window + deploy all? Are there extra steps you are taking before or after deployment?

Using deployment window + new API:

  • Click Services/Deployment
  • Click MyCustomTool/RegisterAPICallbacks
  • Click Deploy on Deployment Window

that will build, deploy, create the dll when building, and my callback will copy the dll over

Using my window:

  • Click MyCustomTool/Deployment
  • Click deploy on my custom window

avoiding that single annoying step LOL yes - im that annoying regarding repetition. its a step that i can see my team missing all the time.

That, and of course - i can add more stuff, like a single button to upload other things, parse editor content to remote configs, etc…so i would already have a window either way :slight_smile:

This is weird, if you have repro steps we could take a look, I will try to repro as-is.

I was unsure on how to just add the callbacks, as there was nothing clear on the expected approach to subscribing to the event. so it might just be me being short-sighted

Code
        [InitializeOnLoadMethod]
        static void OnProjectLoadedInEditor()
        {
            Deployments.Instance.DeploymentWindow.DeploymentEnded += OnDeploymentCompleted2;
        }

        private static void OnDeploymentCompleted2(IReadOnlyList<IDeploymentItem> list)
        {
            Debug.Log(1);

        }

Error
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: Deployment Window must be opened for this operation.

The itemlist is a wildcard *.cs. As long as the DTOs are in the same directory it will just work.

problem here is that my folder organization would be:
Assets/Modules/ModuleX/Scripts/DTO/PayloadX.cs
Assets/Modules/ModuleY/Scripts/DTO/PayloadY.cs

Forcing me to either change my folder structure to accomodate that, or maintain that list whenever there is a new module…
Now - that is my standard, which sounds like my problem :crazy_face:

My understanding right now is that you’re copying over a dll from your CloudCode solution to the assets folder, correct?

Yes, but automatically through the deployment api - so there is nothing i have to do. and the DTOS are really JUST DTO simple classes almost no logic. so although i understand the concern with net6, it has not been a problem as long as i treat DTO as DTO without trying anything fancy.

I’d be happy to try and understand better your use case and help out more, if you want to spend the time of course, as you are probably not the only one with the use-case.

Its a pleasure to help, although my solution is working if we can eventually have everything native would be so much better for future projects and new users! if there is any better communication channel i can use it as well.

Ok, yea makes sense. We can do two things:

  • Events registered to the “unopened” window should trigger. This is a bug IMO and should be easily fixed, you can then register whenever, and they will fire regardless of when they are registered, even before opened. Makes sense, should have tested that
  • Allow to fire deployments without the deployment window opened. More of a feature

problem here is that my folder organization would be:

If you want, give this a try
<Compile Include="../../Assets/**/DTO/**/*.cs" />

Otherwise, I recommend adding

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
     ....
    <!-- This matches the editor LangVersion -->
    <LangVersion>9</LangVersion>

To restrict the language version. Im curious how you are loading .net6 dlls in the editor, unless you’re copying code, and not dlls?

I’ll get the events fixed for next version :slight_smile:

  • On deploy/build, the DLLS for the DTO project get generated

  • Using callbacks, i copy the file and i move then to unity project, no fancy shit - just FileCopy, code can be seen above in the previous answers in my window snippet

Code here is JUST DTO

I might be missing something super obvious here, but its working LOL

Hi! If it works for you thats ok, but I’m wondering what your DTO.csproj looks like.
The framework should be probably restricted to 4.6 or .netstandard 2

I still recommend just including them like so, it will skip your copy process, it would be simpler,
and you’d have no copying. Its more flexible in the long run too.

 <Compile Include="../Backend/DTO/**/*.cs" />

Cheers!