New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unreal: decouple UIs to separate process #4075
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haven't tested it, but code looks good, there are some docstrings missing here and ther though.
And was wondering (in another PR) would be worth wrapping all the functions we pass to send_request
the same way we do with containerise
.
@classmethod | ||
def client(cls): | ||
if not cls.communicator: | ||
return None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a warning
here?
|
||
@classmethod | ||
def execute_george(cls, george_script): | ||
"""Execute passed goerge script in TVPaint.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
george
or goerge
? Anyway what's this exactly? Seems to be TVPaint related?
class BaseUnrealRpc(JsonRpc): | ||
def __init__(self, communication_obj, route_name="", **kwargs): | ||
super().__init__(**kwargs) | ||
self.requests_ids = collections.defaultdict(lambda: 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be this the same as doing collections.defaultdict(int)
? Im not that versed in defaultdict
, so not sure what this is initializing it as?
) | ||
result = future.result() | ||
|
||
not_found = object() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this could be: response = not_found = object()
log.warning("- item is already processed") | ||
return | ||
|
||
callback = self.callback |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these variable assignments necessary?
|
||
def update_context_data(self, data, changes): | ||
content_path = unreal.Paths.project_content_dir() | ||
op_ctx = content_path + CONTEXT_CONTAINER | ||
attempts = 3 | ||
for i in range(attempts): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use enumerate
here so we don't need to "calculate" the index (really a subjective thing):
for i, _ in enumerate(range(attempts)):
try:
with open(op_ctx, "w+") as f:
json.dump(data, f)
break
except IOError as e:
if i == attempts - 1:
raise IOError(
"Failed to write context data. Aborting.") from e
unreal_log(
"Failed to write context data. Retrying...",
"warning"
)
time.sleep(3)
continue
@@ -152,67 +154,54 @@ def _register_events(): | |||
pass | |||
|
|||
|
|||
def send_request(request: str, params: dict = None): | |||
communicator = CommunicationWrapper.communicator | |||
if ret_value := ast.literal_eval( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we jsut in case wrap this with try...except
? In case it's not able to eval
...
@@ -314,8 +314,8 @@ def create_unreal_project(project_name: str, | |||
raise NotImplementedError("Unsupported platform") | |||
if not python_path.exists(): | |||
raise RuntimeError(f"Unreal Python not found at {python_path}") | |||
subprocess.check_call( | |||
[python_path.as_posix(), "-m", "pip", "install", "pyside2"]) | |||
# subprocess.check_call( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is not needed, shall we delete it?
@@ -49,178 +44,48 @@ def create_with_new_sequence( | |||
): | |||
# If the option to create a new level sequence is selected, | |||
# create a new level sequence and a master level. | |||
root = "/Game/Ayon/Sequences" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be root = f"{self.root}/Sequences"
?
return ( | ||
next( | ||
( | ||
loader for loader in loaders if loader.__name__ == name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably unpack this so it's easier to follow/readable
launcher works as expected to launch Unreal with AYON But the Unreal can't open the ayon publisher as it hits the Attribute Error.
The detail log command from UE shown below: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I've been testing it in UE 5.3 (so I've made some changes in ynput/ayon-unreal-plugin). Anyway, I've hit few issues:
Publisher
Creating camera
When running camera creator:
Traceback (most recent call last):
File "C:\Users\annat\Documents\Projects\OpenPype\sources\ayon-desktop\openpype\pipeline\create\context.py", line 2030, in _create_with_unified_error
result = creator.create(*args, **kwargs)
File "C:\Users\annat\Documents\Projects\OpenPype\sources\ayon-desktop\openpype\hosts\unreal\plugins\create\create_camera.py", line 27, in create
instance_data["level"] = send_request("get_editor_world")
File "C:\Users\annat\Documents\Projects\OpenPype\sources\ayon-desktop\openpype\hosts\unreal\api\pipeline.py", line 162, in send_request
return ret_value.get("return")
AttributeError: 'str' object has no attribute 'get'
Creating Layout
When running layout creator:
DEBUG:openpype.hosts.unreal.api.communication_server:Sending request to client localhost (get_editor_world, {}) id: 36
WARNING:CreateContext:Failed to run Creator with identifier "io.ayon.creators.unreal.layout". Creator error: 'str' object has no attribute 'get'
Creating Look
When running Look creator:
WARNING:CreateContext:Failed to run Creator with identifier "io.ayon.creators.unreal.look".
Traceback (most recent call last):
File "C:\Users\annat\Documents\Projects\OpenPype\sources\ayon-desktop\openpype\pipeline\create\context.py", line 2030, in _create_with_unified_error
result = creator.create(*args, **kwargs)
File "C:\Users\annat\Documents\Projects\OpenPype\sources\ayon-desktop\openpype\hosts\unreal\plugins\create\create_look.py", line 40, in create
pre_create_data["members"] = send_request(
File "C:\Users\annat\Documents\Projects\OpenPype\sources\ayon-desktop\openpype\hosts\unreal\api\pipeline.py", line 160, in send_request
communicator.send_request(request, params)
File "C:\Users\annat\Documents\Projects\OpenPype\sources\ayon-desktop\openpype\hosts\unreal\api\communication_server.py", line 605, in send_request
return self.websocket_rpc.send_request(
File "C:\Users\annat\Documents\Projects\OpenPype\sources\ayon-desktop\openpype\hosts\unreal\api\communication_server.py", line 291, in send_request
raise Exception("Error happened: {}".format(error))
Exception: Error happened: {'code': -32000, 'message': 'Python Execution in Unreal Failed!', 'data': ''}
Creating Renders:
This happens probably because of json serialization:
WARNING:CreateContext:Failed to run Creator with identifier "io.ayon.creators.unreal.render".
Traceback (most recent call last):
File "C:\Users\annat\Documents\Projects\OpenPype\sources\ayon-desktop\openpype\pipeline\create\context.py", line 2030, in _create_with_unified_error
result = creator.create(*args, **kwargs)
File "C:\Users\annat\Documents\Projects\OpenPype\sources\ayon-desktop\openpype\hosts\unreal\plugins\create\create_render.py", line 97, in create
self.create_with_new_sequence(
File "C:\Users\annat\Documents\Projects\OpenPype\sources\ayon-desktop\openpype\hosts\unreal\plugins\create\create_render.py", line 50, in create_with_new_sequence
master_lvl, sequence, seq_data = send_request(
File "C:\Users\annat\Documents\Projects\OpenPype\sources\ayon-desktop\openpype\hosts\unreal\api\pipeline.py", line 159, in send_request
if ret_value := ast.literal_eval(
File "C:\Users\annat\.pyenv\pyenv-win\versions\3.9.13\lib\ast.py", line 62, in literal_eval
node_or_string = parse(node_or_string, mode='eval')
File "C:\Users\annat\.pyenv\pyenv-win\versions\3.9.13\lib\ast.py", line 50, in parse
return compile(source, filename, mode, flags,
File "<unknown>", line 1
('/Game/Ayon/Sequences/renderGenericMain/renderGenericMain_MasterLevel', '/Game/Ayon/Sequences/renderGenericMain/renderGenericMain.renderGenericMain', {'sequence': <Object '/Game/Ayon/Sequences/renderGenericMain/renderGenericMain.renderGenericMain' (0x00000B2D4B1E8700) Class 'LevelSequence'>, 'output': 'renderGenericMain', 'frame_range': (0, 150)})
^
SyntaxError: invalid syntax
Scene Inventory
This doesn't work - it won't list any loaded instances (ls()
returning empty list). This might be caused by merging changes in ynput/ayon-unreal-plugin though.
Loading
Loading works, as far as I could test it - I would just add timestamp to server console.
TODO:
- We need to test it in AYON
- Resolve conflicts (merge 3.17 in)
Brief description
Run unreal commands from OP through websocket communication.
Description
UE has the ability to have its python to be called remotely. This PR allow us to run all UIs as separete processes that are comunicating with UE. We'll no longer need to install PySide2 to Unreal.