---
title: Replikant Plans
description: Guid explaining the planned changes in the Replicant codebase
---
## Goals:
* Reduce the size and responsibility of the giant blueprints which have to be modified frequently
* Automatically handle the common operations (subscribing to OSC messages, saving data)
* Less manual work later
* Less common Blueprints to modify
* Support Undo/Redo operations (with minimal extra work per feature)
* Extendable, easy to work with system
## Design:
A system built to manage different Object based blueprints. Extendable via creating new Blueprints.
### 1) Handlers:
```cpp=
// Abstract parent class for the Handler blueprint objects
class UDNAHandler : public UObject
{
// Fill it in child class
// to get a HandleOSCMessage() call for the specified addresses
UPROPERTY(EditAnywhere)
TArray<FName> MessagesToHandle;
// ID is one of the members of MessagesToHandle
UFUNCTION(BlueprintImplementableEvent)
void HandleOSCMessage(FName ID, const FOSCMessage& Message);
// We can also add implementable events for any event which
// might need to be handled:
UFUNCTION(BlueprintImplementableEvent)
void OnSceneLoaded();
UFUNCTION(BlueprintImplementableEvent)
void OnSceneReset();
// ...
};
```
* Handler examples:
* SelectionHandler
* TransformHandler
* SkeletalMeshMaterialHandler
* CommandHandler
* etc.
* All the Blueprint children must be placed in `Content/Blueprints/Handlers`
They are parsed from that subfolder and spawned runtime
* To have a new Handler:
* Create a new Blueprint based on `UDNAHandler`
* Add the desired OSC addresses to the `MessagesToHandle` array
* Input handling and anything which requires a component won't work with Objects (e.g. Timelines)
We could have Actor based Handlers as well:
* base Actor class either
* implementing a shared interface with `UDNAHandler`, or
* containing an `UDNAHandler` which forwards the function calls
* they could support both automatic spawn or manual level placement options
* Most of the logic should be in the `Handler` classes, however HandleOSCMessage typically does not execute them directly:
* Handler spawns `UDNACommand` object and sends it to the `CommandHandler`
* `CommandHandler` calls Execute() on the command object
* The object can call functions on the `Handler`
---
### 2) Commands:
```cpp
/**
* Abstract parent class for the Command blueprint objects
* Commands are spawned runtime when they have to be executed
* All parameters which are required for the execution must be passed on spawn
* The Command is sent to the CommandHandler, which handles the execution
*/
class UDNACommand : public UObject
{
/**
* 1.) Store the variables required to revert the execution
* 2.) Execute the command
*
* @return True if the operation succeeded (and it can be reverted later)
*/
UFUNCTION(BlueprintImplementableEvent)
bool Execute();
// Commands which can not fail can implement OnExecute()
bool Execute_Implementation(UDNACommand* Other)
{
OnExecute();
return true;
}
UFUNCTION(BlueprintImplementableEvent)
void OnExecute();
// The command must be undone (using the variables saved in Execute())
UFUNCTION(BlueprintImplementableEvent)
void Revert();
/**
* Commands has to be merged for ongoing operations
* (E.g. if something is changed with a slider
* the command should only be closed when the user releases the slider)
*
* We can get touch state OSC messages and have an int (CommandSequenceID)
* in the CommandHandler increased each time a widget lose touch
* The CommandSequenceIDs are also passed on to the commands on execution
* New Command will be merged to the last one if
* -> SequenceID in Command matches the one in CommandHandler
* -> Last executed Command class is the same as the new command class
* -> CanBeCombinedWith returns true
*/
UFUNCTION(BlueprintImplementableEvent)
bool CanBeCombinedWith(UDNACommand* Other);
bool CanBeCombinedWith_Implementation(UDNACommand* Other) { return true; }
/**
* Called on a command which was already Executed()
* The parameter is a command with the same class but new parameters
*
* The operation must be executed as if the new command is executed
* after the old one, but the result of the Revert() call should be the same
* (It has to restore the app to the state it was before the first command was
* executed).
*
* After ApplyCommand is called the old command is kept, the new one is dropped
* Multiple ApplyCommands can be called after each other
*/
UFUNCTION(BlueprintImplementableEvent)
void ApplyCommand(UDNACommand* Other);
};
```
---
### 3) Save Data:
```cpp
class UDNASaveData : public UObject
{
UPROPERTY(EditAnywhere)
EDNASaveType SaveType = EDNASaveType::Scene;
};
```
###
* Basic logic is similar to `UDNAHandler`:
* All the BP children must go to `Content/Blueprints/SaveData/`
* They are parsed from that subfolder and spawned runtime
* They can be associated with different save files (Scene, Avatar)
* They should only contain data
* The whole class is saved to and loaded from the associated save file automatically
* Can be accessed via Custom editor node: `GetSaveData`
* Input: `UClass` of an `UDNASaveData` child Blueprint
* Output: BP object casted to proper save data class
* `GetAvatarSaveData` variant with index input pin (Sidekicks should also be Avatars)
### 4) Other notes:
Some of the new features we need require bigger changes, they should be handled during the transition to the new system.
* Sidekicks should be handled as Avatars
* Dynamically spawned instead of loading them with streaming levels to be more flexible
* They should support all editing features Avatars do
* multiple Avatars in a scene instead of having one Avatar + Sidekicks
* Multi-selection support:
* When Multiple object is selected the UI commands should be executed on the ones which support the operation
* In-app UI:
* Should use the same interface as the external UI whenever possible
* Custom Widgets for easier event subscription