Files
2025-09-29 00:52:08 +02:00

306 lines
12 KiB
Python
Executable File

"""
Module for accessing configuration and settings.
Provides access to the current Project, User, Environment( Motionbuilder ) and installed Tools.
!!! THIS FILE SHOULD NOT BE UPDATED FROM WILDWEST !!!
This file MUST use RSG.Base.Configuration not RSG.Configuration
"""
import os
# Ensures current thread is set up to run STA COM concurrency model rather than the default MTA.
# This must be called before clr is imported otherwise Qt application initialisation will fail
# if it happens after this. e.g.: "Qt: Could not initialize OLE (error 80010106)" (url:bugstar:5722186)
import ctypes
ctypes.windll.ole32.CoInitialize(None)
import tempfile
import inspect
import socket
import ipaddress
from xml.etree import cElementTree as xml
import clr
clr.AddReference("RSG.Base.Configuration")
from RSG.Base.Configuration import ConfigFactory
# Create our .NET config object which we will extract all configuration settings from
_clrConfig = ConfigFactory.CreateConfig()
#Removing technicolor fall back as they are no more and Ravi doens't work there anymore
#This is a dictionary containing fallback emails if the email is unknown for a studio
QA_OWNER_EMAIL = {}
def FixPath(path):
return str(path).replace("/", "\\")
class ConfigError(Exception):
pass
class RequiredDataMissingError(ConfigError):
pass
class AppConfig(object):
FFMPEG = "2_7_0"
class VirtualProduction(object):
"""Provides access to Virtual Production Paths etc."""
def __init__(self):
class Path(object):
def __init__(self):
self.Root = os.path.join('x:\\', 'virtualproduction')
self.Previz = os.path.join(self.Root, 'PrevizAssets')
self.Path = Path()
class Script(object):
""" Provides access to information about the python script framework for this session of Motionbuilder."""
def __init__(self):
class Path(object):
""" Nested singleton which contains relevant paths for scripts."""
def __init__(self):
currentFile = str(os.path.abspath(inspect.getfile(inspect.currentframe()))).lower()
self.Root = os.path.abspath(os.path.join(os.path.dirname(currentFile), '..'))
self.RockstarRoot = os.path.join(self.Root, "RS")
self.ExternalRoot = os.path.join(self.Root, "External")
self.ToolImages = os.path.abspath(os.path.join(self.Root, "..", "images"))
self.SetupConfig = os.path.join(self.Root, "setup.config")
# Make temp dir if it is missing
if tempfile.tempdir is None:
tempfile.mkdtemp()
self.WingRemoteFilePath = os.path.join(tempfile.tempdir, "mobuRunRemoteScript.txt")
self.MenuConfig = os.path.join(self.Root, 'menu.config')
_Path = Path()
class Exception(object):
def __init__(self):
tree = xml.parse(_Path.SetupConfig)
root = tree.getroot()
self.IgnorableUsers = [element.text.lower()
for element in root.findall("ExceptionConfiguration/IgnoreUserList/Item")]
self.EmailList = [element.text.lower()
for element in root.findall("ExceptionConfiguration/EmailList/Item")]
self.Path = Path()
self.Exception = Exception()
self.TargetBuild = os.getenv('MOTIONBUILDER_MAJOR_VERSION', '2016')
self.TargetCut = os.getenv('MOTIONBUILDER_MINOR_VERSION', '')
self.Profile = os.getenv('MOTIONBUILDER_PROFILE', '')
class Branch(object):
"""Represents a branch within a project."""
def __init__(self, clrBranch ):
self.Name = clrBranch.Name
self.IsDefault = False
self.ClrBranch = clrBranch
class Path:
def __init__(self):
self.Common = FixPath(clrBranch.Common)
self.Build = FixPath(clrBranch.Build)
self.Audio = FixPath(clrBranch.Audio)
self.Code = FixPath(clrBranch.Code)
self.Art = FixPath(clrBranch.Art)
self.Anim = FixPath(clrBranch.Anim)
self.Assets = FixPath(clrBranch.Assets)
self.Processed = FixPath(clrBranch.Processed)
self.Export = FixPath(clrBranch.Export)
self.Metadata = FixPath(clrBranch.Metadata)
self.Path = Path()
class Project(object):
"""Provides access to project specific settings and configuration."""
def __init__(self, clrProject, isCore=False):
self.ClrProject = clrProject
self.branchPathTokens = ["Common", "Build","Audio", "Code","Art","Anim","Assets","Processed","Export","Metadata"]
self.Name = clrProject.Name
self.FriendlyName = clrProject.FriendlyName
self.IsCore = isCore
defaultBranch = None
self.Branches = list()
for clrBranch in clrProject.Branches:
currentBranch = Branch( clrBranch.Value )
self.Branches.append(currentBranch)
if currentBranch.Name == clrProject.DefaultBranchName:
defaultBranch = currentBranch
defaultBranch.IsDefault = True
if defaultBranch is None:
raise RequiredDataMissingError("Missing default branch")
self.DefaultBranch = defaultBranch
self.Branch = self.DefaultBranch
self.AssetPrefix = clrProject.AssetPrefix
class Path:
def __init__(self):
self.Cache = FixPath(clrProject.Cache)
self.Root = FixPath(clrProject.Root)
self.Art = FixPath(defaultBranch.Path.Art)
self.Anim = FixPath(defaultBranch.Path.Anim)
self.Assets = FixPath(defaultBranch.Path.Assets)
self.Processed = FixPath(defaultBranch.Path.Processed)
self.Export = FixPath(defaultBranch.Path.Export)
self.Metadata = FixPath(defaultBranch.Path.Metadata)
self.Path = Path()
def GetBranchByName(self, branchName):
if not branchName:
return None
for branch in self.Branches:
if branch.Name.lower() == branchName.lower():
return branch
return None
class User(object):
def __init__(self, clrConfig):
self.Name = str(clrConfig.Username)
if clrConfig.Studios.ThisStudio:
self.Domain = str(clrConfig.Studios.ThisStudio.Domain)
self.Studio = str(clrConfig.Studios.ThisStudio.FriendlyName)
self.P4ServerDefault = str(clrConfig.Studios.ThisStudio.DefaultPerforceServer)
# self.P4ServerSwarm = str(clrConfig.Studios.ThisStudio.PerforceSwarmServer) No swarm server with old config
self.ExchangeServer = str(clrConfig.Studios.ThisStudio.ExchangeServer)
# Not all the studios have this setup. See $(tools_branch)\etc\globals\studios.meta
try:
self.P4ServerMocap = str(clrConfig.Studios.ThisStudio.PerforceMocapServer)
except:
self.P4ServerMocap = None
else:
self.Studio = None
self.P4ServerDefault = None
self.P4ServerMocap = None
self.P4ServerSwarm = None
self.ExchangeServer = None
self.IpAddress = ipaddress.ip_address(unicode(socket.gethostbyname(socket.gethostname())))
if clrConfig.EmailAddress:
self.Email = clrConfig.EmailAddress.lower()
else:
self.Email = None
self.IsExternal = True
if self.Studio is not None and 'R*' in self.Studio:
self.IsExternal = False
self.IsInternal = not self.IsExternal
if hasattr(clrConfig, 'UserType') and clrConfig.UserType is not None:
self.Type = str(clrConfig.UserType.DisplayName)
else:
self.Type = "Unknown"
@staticmethod
def tempDir():
"""
Wrapper around tempfile function to get the temp directory on Windows.
This handle short paths, ie 'c:\\users\\matthe~1.kap\\appdata\\local\\temp'
See Also:
https://web.archive.org/web/20131206010029/http://support.microsoft.com/kb/142982
Returns:
unicode
"""
maxWindowsPathSize = 260
pathBuffer = ctypes.create_unicode_buffer(maxWindowsPathSize)
shortTempDir = tempfile.gettempdir()
retVal = ctypes.windll.kernel32.GetLongPathNameW(unicode(shortTempDir), pathBuffer, maxWindowsPathSize)
if retVal == 0:
raise OSError("Failed to resolve long path for temp dir: {}".format(shortTempDir))
return pathBuffer.value
class Tool(object):
""" Nested singleton which contains relevant paths for tools. """
def __init__(self, clrConfig):
class Path:
def __init__(self):
self.Bin = FixPath(clrConfig.ToolsBin)
self.Logs = FixPath(os.path.join(str(clrConfig.ToolsLogs), 'Motionbuilder'))
self.Root = FixPath(clrConfig.ToolsRoot)
self.Config = FixPath(clrConfig.ToolsConfig)
self.Temp = FixPath(clrConfig.ToolsTemp)
self.TechArt = FixPath(os.path.join(self.Root, "techart"))
currentFilePath = FixPath(os.path.abspath(inspect.getfile(inspect.currentframe()))).lower()
if r'x:\wildwest' in currentFilePath:
self.TechArt = r'x:\wildwest'
targetBuild = os.getenv('MOTIONBUILDER_MAJOR_VERSION', '2016')
self.MobuRoot = os.path.join(self.Root,
"dcc",
"current",
"motionbuilder{0}".format(targetBuild))
self.Assemblies = os.path.join(self.MobuRoot, "assemblies", "x64")
self.Plugins = os.path.join(self.MobuRoot, "plugins", "x64")
self.Path = Path()
# Parse core project first
CoreProject = Project(_clrConfig.Project,True)
# Flesh out DLC projects and TitleUpdateProject
DLCProjects = dict()
TitleUpdateProject = None
for _dlcProject in _clrConfig.DLCProjects.Values:
try:
dlcProject = Project(_dlcProject)
DLCProjects[_dlcProject.Name] = dlcProject
if _dlcProject.Name == "titleupdate":
TitleUpdateProject = Project(_dlcProject)
except RequiredDataMissingError:
# Ignore projects with missing config data
# e.g. the project's .xml file does not exist (url:bugstar:5370440)
pass
User = User(_clrConfig)
Tool = Tool(_clrConfig)
VirtualProduction = VirtualProduction()
Script = Script()
def GetProjectByName(projectName):
'''Searches through DLC projects and Core project. Returns the project that matches the supplied projectname'''
for project in [CoreProject] + DLCProjects.values():
if projectName.lower() == project.Name.lower():
return project
return None
#We will now attempt to switch the currently targeted project based off environment vars (if they are present)
Project = CoreProject
if 'RS_PROJECT' in os.environ:
#Only searching through the DLC packs. If it doesn't find a pack/project with a matching name it will fall back to core
for dlcProject in DLCProjects.itervalues():
if dlcProject.Name == os.environ['RS_PROJECT']:
Project = dlcProject
break
if 'RS_BRANCH_NAME' in os.environ:
Project.Branch = Project.GetBranchByName(os.environ['RS_BRANCH_NAME'])
CoreProject.Branch = CoreProject.GetBranchByName(os.environ['RS_BRANCH_NAME'])