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

1025 lines
33 KiB
Ruby
Executable File

#
#
# Author:: Mark Harrison-Ball <Mark.Harrison-Ball@rockstargames.com>
# Date:: 20 Februray 2013 (AP3)
# Purpose:
# Will read through the tracking docs and extract approved data and bugnumbers
# WIll then updat ethe meta data file used by the game to display if something has been approved
# Will also generate a email report sent to producrion
# Is deiged to run as a automated task checking perforce for checked in file
#
#-----------------------------------------------------------------------------
# Uses
#-----------------------------------------------------------------------------
#include RSG::Base::Logging
#include RSG::Base::Logging::Universal
require 'RSG.Base.dll'
require 'RSG.Base.Configuration.dll'
require 'RSG.Base.Windows.dll'
require 'RSG.SourceControl.Perforce'
require 'RSG.Metadata'
# Core
include RSG::Base::Configuration
include RSG::Base::Logging
include RSG::Base::Logging::Universal
include RSG::Base::OS
include RSG::SourceControl::Perforce
include RSG::Metadata
require 'mscorlib'
require 'System.Core'
require 'System.Xml'
require 'win32ole'
include System::Collections::Generic
include System::IO
include System::Diagnostics
include System::Xml
include System::Runtime::InteropServices
include System::Net
include System::Net::Sockets
include System::Net::Mail
require 'pipeline/os/options'
#require 'pipeline/util/email'
include Pipeline
DEBUG = false
#-----------------------------------------------------------------------------
# Constants
#-----------------------------------------------------------------------------
OPTIONS = [
LongOption::new( 'frequency', LongOption::ArgType.Required, 'f',
'frequency to check perforce for submits(required).' )
]
#EMAIL = 'mark.harrison-ball@rockstargames.com'
EMAIL = 'AutoApprovedCutsceneUpdater@rockstargames.com'
AUTHOR = ['mark.harrison-ball@rockstargames.com']
#SERVER = 'rsgldnexg1.rockstar.t2.corp'
@server = 'rsgldnexg1.rockstar.t2.corp'
#SMTPSERVER.Port = 25
PRODUCTION_EMAIL = ['dermot.bailie@rockstarnorth.com',
'tina.chen@rockstargames.com',
'sam.henman@rockstargames.com',
'francesca.howard@rockstarnorth.com',
'eric.smith@rockstarsandiego.com',
'sean.letts@rockstarsandiego.com',
'mark.harrison-ball@rockstargames.com']
=begin
PRODUCTION_EMAIL = ['mark.harrison-ball@rockstargames.com',
'mark.harrison-ball@rockstargames.com']
=end
DOF_COUNT = 0
LIGHTING_COUNT = 0
FACIAL_COUNT = 0
ANIMATION_COUNT = 0
CAMERA_COUNT = 0
# Total entries
CUTLIST_COUNT = 0
FACIALLIST_COUNT = 0
#-----------------------------------------------------------------------------
# Functions
#-----------------------------------------------------------------------------
#CUTSCENEMETA = "X:/gta5/build/dev/common/non_final/cutscene/ApprovedCutsceneList.meta"
=begin
Perfore functions
=end
#
#--------------------------------------------------------
#def send_email( server, port, subject, body, from, to, reply_to = '',
# from_alias = '', to_alias = '', reply_to_alias = '' )
def send_email(from, to_list, subject, message)
#port= 25
#server= 'rsgnycexg04.rockstar.t2.corp'
#subject='test'
#body= 'test'
#from= 'mark.harrison-ball@rockstargames.com'
#to= 'matt.tempest@rockstarlondon.com'
#to = 'mark.harrison-ball@rockstargames.com'
begin
Mail = System::Net::Mail::MailMessage.new
Smptserver = System::Net::Mail::SmtpClient.new(@server)
Smptserver.Port = 25
MailAddress = System::Net::Mail::MailAddress.new(from)
Mail.From = MailAddress
to_list.each do |to|
Mail.To.Add(to)
end
Mail.IsBodyHtml = true
Mail.Subject = subject
Mail.Body = message
Smptserver.Send(Mail);
rescue => msg
puts(msg)
end
end
#---------------------------------------------------------------------------------------------------------------
# REPORT GENERTATION
#---------------------------------------------------------------------------------------------------------------
# Convert bool to symbol
#---------------------------------------------------
def returnHtmlBoolSymbol(bFlag)
htmlString = ""
if bFlag.class == String.class
htmlString = "<span>#{bFlag}</span>"
elsif bFlag.class == true.class
htmlString = "<span style=\"font-family: wingdings; color=\"#556B2F\"; font-size: 300%;\">&#252;</span>"
elsif bFlag.class == false.class
htmlString = "<span style=\"font-family: wingdings; font-size: 140%; color=\"#FFE4C4\" \">&#251;</span>"
end
htmlString
end
def returnHtmlUrlLink(iValue)
htmlString = "-"
if not iValue == "-"
bNumber = iValue.to_i
htmlString = "<a href=\"url:bugstar:#{bNumber}\">#{bNumber}</a>"
end
htmlString
end
# Create HTML HEader
#----------------------------------------------
def HtmlHeader()
@msgBody << "<div><span style='font-size:20;font-family:\"Arial\",\"sans-serif\";color:windowtext'><b>Cutscene Tracking Changes > ApprovedCutsceneList.meta</b></span></div>"
@msgBody << "<div><span style='font-size:9.0pt;line-height:115%;font-family:\"Arial\",\"sans-serif\"'>Date #{Time.now}<o:p></o:p></span></div><br/><br/>"
@msgBody << "<table width=\"800\" border=1 cellspacing=\"0\">
<tr><td style= \"align=\"left\"; colspan=3; bgcolor=\"#B22222\"><font size=\"2\" face=\"verdana\" color=\"white\"><p><b>Report generated from</b></p></td></tr>
<tr><td style= \"align=\"left\"; bgcolor=\"#FFE4C4\"><span><font size=\"1\" face=\"verdana\">//depot/gta5/docs/production/GTAV_CAMERA_TRACKING.xlsm</span></td></tr>
<tr><td style= \"align=\"left\"; bgcolor=\"#FFE4C4\"><span><font size=\"1\" face=\"verdana\">//depot/gta5/docs/production/Animation/V_Facial_Animation_Tracking.xlsm</span></td></tr>
<tr><td style= \"align=\"left\"; bgcolor=\"#FFE4C4\"><span><font size=\"2\" color=\"blue\" face=\"verdana\"><i>changes CL#:#{@lastCL} to #{@currentCL}</i></span></td></tr>
</table><br/>"
end
def HtmlSummary()
width = (800/100.0)
@msgBody << "<div><span style='font-size:20;font-family:\"Arial\",\"sans-serif\";color:windowtext'><b>Cutscene Overall Summary</b></span></div>"
# Camera Approved Percentage
cam_width = ((100.0/CUTLIST_COUNT)*CAMERA_COUNT) * width
@msgBody << "<table width=\"800\" border=0 cellspacing=\"0\"><tr>
<tr><td width=\"#{cam_width}\"; style= \"align=\"left\"; bgcolor=\"#007FFF\"><span><font size=\"1\" face=\"verdana\">Camera Approved</span></td>
<td width=\"#{800-cam_width}\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\">#{((100.0/CUTLIST_COUNT)*CAMERA_COUNT).round}%</td>
</tr></table>"
# DOF Approved Percentage
dof_width = ((100.0/CUTLIST_COUNT)*DOF_COUNT) * width
@msgBody << "<table width=\"800\" border=0 cellspacing=\"0\"><tr>
<tr><td width=\"#{dof_width}\"; style= \"align=\"left\"; bgcolor=\"#FF7F00\"><span><font size=\"1\" face=\"verdana\">DOF Approved</span></td>
<td width=\"#{800-dof_width}\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\">#{((100.0/CUTLIST_COUNT)*DOF_COUNT).round}%</td>
</tr></table>"
# Lighting Approved Percentage
lig_width = ((100.0/CUTLIST_COUNT)*LIGHTING_COUNT) * width
@msgBody << "<table width=\"800\" border=0 cellspacing=\"0\"><tr>
<tr><td width=\"#{lig_width}\"; style= \"align=\"left\"; bgcolor=\"#5F9F9F\"><span><font size=\"1\" face=\"verdana\">Lighting Approved (2nd pass)</span></td>
<td width=\"#{800-lig_width}\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\">#{((100.0/CUTLIST_COUNT)*LIGHTING_COUNT).round}%</td>
</tr></table>"
# Animation Approved Percentage
anm_width = ((100.0/CUTLIST_COUNT)*ANIMATION_COUNT) * width
@msgBody << "<table width=\"800\" border=0 cellspacing=\"0\"><tr>
<tr><td width=\"#{anm_width}\"; style= \"align=\"left\"; bgcolor=\"#6B8E23\"><span><font size=\"1\" face=\"verdana\">Animation Approved</span></td>
<td width=\"#{800-anm_width}\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\">#{((100.0/CUTLIST_COUNT)*ANIMATION_COUNT).round}%</td>
</tr></table>"
# Facial Approved Percentage
fce_width = ((100.0/FACIALLIST_COUNT)*FACIAL_COUNT) * width
@msgBody << "<table width=\"800\" border=0 cellspacing=\"0\"><tr>
<tr><td width=\"#{fce_width}\"; style= \"align=\"left\"; bgcolor=\"#A68064\"><span><font size=\"1\" face=\"verdana\">Facial Approved</span></td>
<td width=\"#{800-fce_width}\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\">#{((100.0/FACIALLIST_COUNT)*FACIAL_COUNT).round}%</td>
</tr>
</table><br/>"
end
# Create Table Info
#----------------------------------------------
def generateBaseTable(statusType,headerName, color)
@msgBody << "<table width=\"800\" cellspacing=\"0\">
<tr><td style= \"align=\"left\"; bgcolor=\"#{color}\"><font size=\"2\" face=\"verdana\" color=\"white\"><p><b>#{headerName}</b></p></td>
<td style= \"align=\"left\"; bgcolor=\"#{color}\"> </td>
<td style= \"align=\"left\"; bgcolor=\"#{color}\"> </td>
<td style= \"align=\"left\"; bgcolor=\"#{color}\"> </td>
</tr>
<tr>
<td width=\"250\"; height=\"20\" style= \"align=\"left\"; bgcolor=\"#FFE4C4\"><span><font size=\"1\" face=\"verdana\"><b>Cutscene Name</span></td>
<td width=\"300\"; style= \"align=\"left\"; bgcolor=\"#FFE4C4\"><span><font size=\"1\" face=\"verdana\"><b>""</span></td>
<td width=\"100\"; style= \"align=\"left\"; bgcolor=\"#FFE4C4\"><span><font size=\"1\" face=\"verdana\" ><b>Status</span></td>
<td width=\"150\"; style= \"align=\"left\"; bgcolor=\"#FFE4C4\"><font size=\"1\" face=\"verdana\"><b>Bug Number</td>
</tr>
"
@emailLog.each do |key, value |
# loop through scene Name attributes
scenename = key
hasMultipleStatus = false
# We need to find out how many values are set to true, if more than 1 then we sett key and loop else
index = 0
value.each do |attr |
if attr.has_value?(statusType)
index+=1
end
end
# we have multiple approved values set the same
if index > 1
hasMultipleStatus = true
end
# We loop each list of attr
value.each do |attr |
if attr.has_value?(statusType)
approvename = attr[:ApprovedName]
status = attr[:Status]
bugnumbers = attr[:Bug]
@msgBody << "<tr>
<td style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\"><b>#{scenename}</b></td>
<td style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\"><i>#{approvename}</i></td>
<td style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\">#{returnHtmlBoolSymbol(status)}</td>
<td style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\">#{returnHtmlUrlLink(bugnumbers[0])}</td>
</tr>"
if bugnumbers.size > 1
(1..bugnumbers.size-1).each do |n|
@msgBody << "<tr>
<td width=\"250\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\"></td>
<td width=\"300\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\"></td>
<td width=\"100\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\"></td>
<td width=\"150\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\">#{returnHtmlUrlLink(bugnumbers[n])}</td>
</tr>"
end
end
if hasMultipleStatus == true
scenename = ""
hasMultipleStatus == false
end
end
# SPACER
end
if index > 0
@msgBody << "<tr>
<td height=\"6\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\"></td>
<td height=\"6\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\"></td>
<td height=\"6\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\"></td>
<td height=\"6\"; style=\"align=\"left\"; bgcolor=\"#EAEAAE\"><font size=\"2\" face=\"verdana\"></td>
</tr>"
end
end
@msgBody << "</table><br/>"
end
def closeHtmlHeader()
@msgBody << "<div><span style='font-size:8.0pt;line-height:115%;font-family:\"Arial\",\"sans-serif\" line-height: 0%;'><b>Generated by //depot/gta5/tools/wildwest/script/Ruby/ApprovedCutsceneUpdater/CutsceneApprovedUpdater.rb</b></span></div>"
@msgBody << "<div><span style='font-size:8.0pt;line-height:115%;font-family:\"Arial\",\"sans-serif\" line-height: 0%;'>Author: Mark.Harrison-Ball@rockstargames.com</span></div>"
end
def generateEmailReport()
HtmlHeader()
HtmlSummary()
generateBaseTable(true,"Approved Changes",'#556B2F')
generateBaseTable(false,"Not Approved Changes",'B22222')
generateBaseTable("new", "Name Changes",'#FFA500')
closeHtmlHeader()
if DEBUG
aFile = File.new("x:/temp/test.html", "w")
aFile.write(@msgBody)
aFile.close
end
return @msgBody
end
#---------------------------------------------------------------------------------------------------------------
#
#---------------------------------------------------------------------------------------------------------------
# Return Changelist objects from a set of Change records.
#---------------------------------------------------------
def get_changelists_from_changes( log, p4, change_records )
changelists = []
change_records.each do |change_record|
changelist = Changelist::Create( p4, change_record['change'].to_i )
changelists << changelist[0]
end
changelists
end
# Return the last submitted changelist number in perforce
#--------------------------------------------------------
def get_current_changelistnumber(p4, log)
change_records = p4.Run( 'changes', true, '-m', 1.to_s )
changes = get_changelists_from_changes( log, p4, change_records )
changes[0].Number
end
#
#---------------------------------------------------------------------------------
def parse_p4_review(p4, depotpath, log)
log.message( "Scanning for changes since last Changelist = #{@currentCL}.....")
g_Status = 'submitted'
bRequireCutsceneUpdate = false
change_records = p4.Run( 'changes', true, '-s', g_Status, depotpath+'@'+@currentCL.to_s+',#head' )
changes = get_changelists_from_changes( log, p4, change_records )
changes.each do |change|
log.message( "Changelist: #{change.Number} by #{change.Username}" )
if change.Number > @currentCL
@currentCL = (change.Number+1)
log.message( "Last Changelist = #{@currentCL}")
end
change.Files.each do |filename|
file_name = File.basename(filename).downcase
if file_name == 'gtav_camera_tracking.xlsm' or file_name == 'v_facial_animation_aracking.xlsm'
@p4_infodescription += "\nChangelist: #{change.Number} File: #{file_name} by #{change.Username}"
bRequireCutsceneUpdate = true
end
log.Message("\t#{filename}" )
end
end
bRequireCutsceneUpdate
end
# Revert if unchanged and tehn submit
#---------------------------------------------------------------
def submit_p4_file(p4, file, changelist, log)
# revert if unchnaged
log.message( "Reverting unchanged files for changelist ##{changelist.Number.to_s}")
reverted = p4.Run( 'revert', true, '-a', '-c', changelist.Number.to_s)
if (reverted.count == 0) # means file was NOT reverted
log.message( "Submitting changelist ##{changelist.Number.to_s}")
reportmsg = generateEmailReport()
if DEBUG
send_email(@email, AUTHOR, 'Auto Approved Cutscene Updated', reportmsg)
else
p4.Run( 'submit', true, '-c', changelist.Number.to_s )
send_email(@email, PRODUCTION_EMAIL, 'Auto Approved Cutscene Updated', reportmsg)
end
else
log.message( "Deleting empty changelist ##{changelist.Number.to_s}")
changelist.delete
end
end
#---------------------------------------------------
# Generic Excel Parser
# Input: log / not nessecary
# i: filename
# i: rowkey - a column index to look up and the value to seach for
# i: excelcolumnindexlist - List of column xcel names to collect
#---------------------------------------------------
#def ParseExcelDoc(excelfilename, rowkey, excelcolumnindexlist, log)
def ParseExcelDoc(excelfilename, rowkey, log)
# Checks
break if rowkey.length == 0
excel = WIN32OLE::new('excel.Application')
workbook = excel.Workbooks.Open(excelfilename)
worksheet = workbook.Worksheets(1)
worksheet.Select
excel['Visible'] = false #make visible, set to false to make invisible
rows = worksheet.UsedRange.Rows.Count;
cols = worksheet.UsedRange.Columns.Count;
aArray = Array.new()
eArray = Array.new()
eSubArray = Array.new()
excelStatus = 0
if rowkey[:key]
key_name = rowkey[:subkey]
end
if rowkey[:subkey]
subkey_name = rowkey[:subkey]
end
#aArray = Array.new()
# We want to go trhough each row, if we have a key then read the rwo with the key
count = 0
rows.times do |count|
key = rowkey[:row] +(count+1).to_s
masterLookUpVal = worksheet.range(key).value
if masterLookUpVal != nil and masterLookUpVal.is_a? String
if masterLookUpVal.downcase == rowkey[:key]
if eArray.size > 0 # if we had a previous array
if eSubArray.size > 0
eArray << eSubArray
end
aArray << eArray
eArray = Array.new()
eSubArray = Array.new()
end
excelStatus = 1
elsif masterLookUpVal.downcase == rowkey[:subkey] # we say look fo rsub info
excelStatus = 2
else
excelStatus = 0
end
else
excelStatus = 0
end
# Master Data
if excelStatus == 1
rowkey[:vals].each do |indexVal|
lookupColIndex = indexVal+(count+1).to_s()
val_toAdd = worksheet.range(lookupColIndex).value
#puts(val_toAdd)
eArray << val_toAdd
end
end
# Subdata
if excelStatus == 2
rowkey[:subvals].each do |indexVal|
lookupColIndex = indexVal+(count+1).to_s()
val_toAdd = worksheet.range(lookupColIndex).value
eSubArray << val_toAdd
end
end
if count == rows.size-1
# Add last array
if eArray.size > 0 # if we had a previous array
if eSubArray.size > 0
eArray << eSubArray
end
aArray << eArray
end
end
end
workbook.Close(false)
excel.Quit
excel = nil
#GC.start # Invoke
return aArray
end
=begin
masterLookUpVal = testarray[i][rowkey[:row]]
datVal = worksheet.range(key).value
if datVal != nil
begin
#puts(datVal)
if rowkey[:keys].include?(datVal.downcase)
if datVal.downcase == rowkey[1].downcase # We found a key
# Read each Header value into List
eArray = Array.new()
excelcolumnindexlist.each {|colkey|
lookupColIndex = colkey+(count+1).to_s()
val = worksheet.range(lookupColIndex).value
eArray <<(val)
}
aArray << eArray
end
rescue
log.Message("Skipping Value #{datVal}")
end
end
end
=end
#---------------------------------------------------
# Read our Tracking Doc
# Ok I need to change this so we look up for the actual name than the key
#---------------------------------------------------
def ReadTrackingDoc(log, doc)
log.Message("Opening Doc #{doc}")
#rowkey = ["B","cs"] # optional row Key
#rowkey = {:row => "B", :keys =>["cs", "fbx"]} # optional row Key
#excelcolumnindexlist = ["d", "u", "y", "z","az","ba"]
rowkey = {:row => "b",
:key => "cs",
:vals => ["d", "t", "x", "y","be","bf","v"],
:subrow => "b",
:subkey => "fbx",
:subvals => ["i"]
}
# 0|D = Scene Name
# 1|2 T/U = Edit Approved
# 2|X = bug number for DOF
# 3|Y = Dof approved
# 4|BE = bug number for lighting
# 5|BF = LIghting approved (2st Pass)
# 6|V = Animation
#ParseExcelDoc(doc, rowkey, excelcolumnindexlist,log)
ParseExcelDoc(doc, rowkey, log)
end
#---------------------------------------------------
# Read our Facial Doc
#--------------------------------------------------
def ReadFacialDoc(log, doc)
log.Message("Opening Doc #{doc}")
#rowkey = {:row => "f", :keys =>["face"]} # optional row Key
#excelcolumnindexlist = ["ak","ai", "c"]
# 0 Scene Name
# 1 SOFT Approved
# 2 = bugnumber
rowkey = {:row => "g",
:key => "face",
:vals => ["al","aj","c"]
}
#ParseExcelDoc(doc, rowkey, excelcolumnindexlist, log)
ParseExcelDoc(doc, rowkey, log)
end
#---------------------------------------------------
# Convert value to Bool
#---------------------------------------------------
def convertToBool(value, log, valName)
log.Message("#{valName} = #{value}")
if (value.class == System::DateTime)
return true
elsif (value == 1.0)
return true
elsif (value == 'Test (Verifying)')
return true
elsif (value == 'Closed (Fixed)')
return true
elsif (value == 'Dev (Fixed, Awaiting Build)')
return true
else
return false
end
end
#---------------------------------------------------
# Convert value to Bool
#---------------------------------------------------
def convertDateToBool(value, log, valName)
log.Message("#{valName} = #{value.to_s}")
#log.Message("#{valName} = #{value.class}")
#puts(value.class)
if (value.class == System::DateTime)
return true
else
return false
end
end
=begin
def LogValueHasChanged(scenename, cutlistentryValue, exceldataEntryValue, cutlistentry)
if cutlistentryValue.value != bcutval2
if not @emailLog.include?('chees33e')
end
else
@emailLog['cheese'] = ['cutscneParroved = true']
end
end
cutentry.value = bcutval2
end
=end
def valueBoolChanged(bInputA, bInputB)
bHasChanged = false
if bInputA != bInputB
bHasChanged = true
end
bHasChanged
end
def updateMetaEntry(cutlistentry, approvedName, exceldataValue, log, excelbugnumbersArray = ['-'])
scenename = cutlistentry["CutsceneName"].value
bApprovedFlag = cutlistentry[approvedName].value
bExcelFlag = convertToBool(exceldataValue, log, approvedName)
if approvedName == "CameraApproved" and bExcelFlag
CAMERA_COUNT += 1
elsif approvedName == "LightingApproved" and bExcelFlag
LIGHTING_COUNT +=1
elsif approvedName == "FacialApproved" and bExcelFlag
FACIAL_COUNT +=1
elsif approvedName == "AnimationApproved" and bExcelFlag
ANIMATION_COUNT +=1
elsif approvedName == "DofApproved" and bExcelFlag
DOF_COUNT += 1
end
if valueBoolChanged(bApprovedFlag, bExcelFlag)
cutlistentry[approvedName].value = bExcelFlag
if not @emailLog.include?(scenename)
@emailLog[scenename] = [{:ApprovedName => approvedName, :Status => bExcelFlag, :Bug => excelbugnumbersArray}]
else
@emailLog[scenename] << {:ApprovedName=> approvedName, :Status => bExcelFlag, :Bug => excelbugnumbersArray}
end
end
end
#------------------------------------
#
#-----------------------------------
# 0 D = Scene Name
# 1|2 U|V = Edit Approved
# 2 bug number for lighting
# 3 Z =Dof approved
# 4 bug number for lighting
# 5 LIghting approved (2st Pass)
def updateMetaData(scenename, exceldataEntry, cutlistentry, log)
#updateMetaEntry(cutlistentry, "CutsceneApproved", exceldataEntry[?], log)
#log.message("CameraApproved = #{exceldataEntry[1]}, DofApproved = #{exceldataEntry[3]}, bug = #{[exceldataEntry[2]].to_s}, LightingApproved = #{exceldataEntry[5].to_s}, bug = #{[exceldataEntry[4]].to_s}")
#print(exceldataEntry+"\n")
updateMetaEntry(cutlistentry, "AnimationApproved", exceldataEntry[6], log )
updateMetaEntry(cutlistentry, "CameraApproved", exceldataEntry[1], log, exceldataEntry.last )
updateMetaEntry(cutlistentry, "DofApproved", exceldataEntry[3], log, [exceldataEntry[2]])
updateMetaEntry(cutlistentry, "LightingApproved", exceldataEntry[5], log, [exceldataEntry[4]])
end
# 0 = bugnumbers
# 1 SOFT Approved
# 2 Scene Name
def updateFaceMetaData(scenename, exceldataEntry, cutlistentry, log)
updateMetaEntry(cutlistentry, "FacialApproved", exceldataEntry[0] , log, exceldataEntry[1] )
end
#-------------------------------------------------------
# Add a new tunable to our metadata file
#-------------------------------------------------------
def AddCutsceneEntry(name, memberUtils, cutlist, metaLoad)
member = memberUtils.FindStructMemberByName(metaLoad.Definition.Members, "ApprovalStatuses")
structTunable = RSG::Metadata::Data::StructureTunable.new(member, nil)
structTunable.Item("CutsceneName").value = name
cutlist.Add(structTunable)
return structTunable
end
def UpdateCutscene_osi(p4, branch, g_Log)
excelTrackfilename = 'X:/gta5/docs/production/GTAV_CAMERA_TRACKING.xlsm'
excelFacialfilename = 'X:/gta5/docs/production/Animation/V_Facial_Animation_Tracking.xlsm'
metaCameraFile = 'X:/gta5/build/dev/common/non_final/cutscene/ApprovedCutsceneList.meta'
definationsPath = 'x:/gta5/assets/metadata/definitions/'
#------------------------------------
# Sync to required Files
#args = ['-f', Path.Combine( dest_dir, '...' )
g_Log.Message('Found changes, syncing Files')
args = ['-f', excelTrackfilename, excelFacialfilename, metaCameraFile, ( definationsPath+ '...' )]
p4.Run( 'sync', System::Array[System::String].new(args) )
changelist_description = 'Auto Approved Cutscene Update Submit [AP3]' +@p4_infodescription
changelist = p4.CreatePendingChangelist(changelist_description)
main_args = ['-c', changelist.Number.to_s]
args = main_args + [metaCameraFile]
args = System::Array[System::String].new(args)
ret = p4.Run('edit', args)
#g_Log.Message(args)
#_Log.Message(ret)
DOF_COUNT = 0
LIGHTING_COUNT = 0
FACIAL_COUNT = 0
ANIMATION_COUNT = 0
CAMERA_COUNT = 0
# Total entries
CUTLIST_COUNT = 0
FACIALLIST_COUNT = 0
begin
#----------------------------------
trackingData = ReadTrackingDoc(g_Log, excelTrackfilename)
facialData = ReadFacialDoc(g_Log, excelFacialfilename)
structspath = [definationsPath]
structPathNet = System::Array[System::String].new(structspath)
structDict = RSG::Metadata::Model::StructureDictionary.new
structDict.load(branch, structPathNet)
# put in beign catch
g_Log.Message("loaded Definitions sucessfully")
metaLoad = RSG::Metadata::Model::MetaFile.new( metaCameraFile, structDict, true)
# Static Def calsses
TunableUtils = RSG::Metadata::Util::Tunable
MemberUtils = RSG::Metadata::Util::Member
# Main Dictionary
cutlist = TunableUtils.FindFirstStuctureNamed("ApprovalStatuses", metaLoad.Members )
# Create a array of scene names so we can do quick lookup from the cutlist.
metasceneNames = []#Hash.new
cutlist.each {|colkey|
metasceneNames << colkey["CutsceneName"].value
}
g_Log.message("======================================CutNames lookup = #{metasceneNames.length}")
g_Log.Message('Created look up')
#------------ Check data
CUTLIST_COUNT = trackingData.length
#------------------------------
trackingData.each{|thisdata|
# Check if cutscene name exists, if does update
lscenename = thisdata[0].downcase
#g_Log.Message(metasceneNames[lsceneName])
if metasceneNames.include?(lscenename)
#if metasceneNames.has_key?(lSceneName)
g_Log.Message("Found Existing cutscene #{lscenename}")
index = metasceneNames.index(lscenename)
updateMetaData(lscenename, thisdata, cutlist[index], g_Log )
# If does not exist add new data
else
g_Log.Message("Creating New Entry for cutscene #{lscenename}")
entry = AddCutsceneEntry(lscenename, MemberUtils, cutlist, metaLoad)
updateMetaData(lscenename, thisdata, entry, g_Log)
#@emailLog[lscenename] = ["","New entry has been added","-"]
@emailLog[lscenename] = [{:ApprovedName => "New entry/Name Change", :Status => "new", :Bug => ["-"]}]
end
# don't really need to care about old data
}
#----------------------------------
# Rebuild our lookup table
metasceneNames.clear #= []#Hash.new
cutlist.each {|colkey|
metasceneNames << colkey["CutsceneName"].value
}
g_Log.message("====================================CutNames lookup= #{metasceneNames.length}")
# So we need to do some colection on the arrays to make sure all characters have got facical passses
g_Log.message("Parsing Facial Doc")
validfacialData = Hash.new
facialData.each{|thisdata|
sceneName = thisdata[0].downcase
if not validfacialData.include?(sceneName)
validfacialData[sceneName] = [thisdata[1], [thisdata[2]]]
else
if (thisdata[1].class != System::DateTime)
validfacialData[sceneName][0] = 0.0
end
validfacialData[sceneName][1] << thisdata[2] #add bugnumbers
end
}
g_Log.Message("validfacialData = #{validfacialData.length} , facialData = #{facialData.length}")
FACIALLIST_COUNT = validfacialData.length
# now lets go theough our filtered down Hash
validfacialData.each do |key, value|
if metasceneNames.include?(key)
g_Log.Message("Found Existing cutscene #{key}")
index = metasceneNames.index(key)
updateFaceMetaData(key, value, cutlist[index], g_Log)
end
end
# Save the new metafile
metaLoad.SerialiseAlt(metaCameraFile)
# revert and submit
submit_p4_file(p4, metaCameraFile, changelist, g_Log)
g_Log.Message("Sucess!")
rescue => msg
puts(msg)
g_Log.error("Crash Deteted ")
send_email(EMAIL, AUTHOR, 'Auto Approved Cutscene Updater', msg)
end
end
#-----------------------------------------------------------------------------
# Entry-Point
#-----------------------------------------------------------------------------
if ( __FILE__ == $0 ) then
g_Options = OS::Options::new( OPTIONS )
LogFactory.CreateApplicationConsoleLogTarget( )
g_Log = LogFactory.ApplicationLog
begin
if ( g_Options.is_enabled?( 'help' ) )
puts "#{__FILE__}"
puts "Usage:"
puts g_Options.usage()
exit( 1 )
end
g_Frequency = g_Options.get?( 'frequency' ) unless g_Options.get( 'frequency' ).nil?
# Perforce initialisation.
g_Config = RSG::Base::Configuration::ConfigFactory::CreateConfig( )
@email = g_Config.EmailAddress
@server= g_Config.studios.this_studio.exchange_server # FUCKING MAKES NOT FUCKING DIFFERNCE!@
@server= 'rsgldnexg1.rockstar.t2.corp'
branch = g_Config.Project.DefaultBranch
p4 = g_Config.Project.SCMConnect()
# Statics
depotpath = '//depot/gta5/docs/production/...'
@currentCL = get_current_changelistnumber(p4, g_Log)
#@currentCL = 3778825
@lastCL = @currentCL
# USed for debug test
if DEBUG
@currentCL = 3757695
end
@currentCL = 3757695
send_email(EMAIL, AUTHOR, 'Auto Approved Cutscene Updater', 'Cutscene Approved Updater has started!')
repeat = true
while repeat
# Globals
@p4_infodescription = ''
@emailLog = Hash.new
@msgBody = ""
# ---------
bRunCutsceneUpdate = parse_p4_review(p4, depotpath, g_Log)
if bRunCutsceneUpdate
#puts("Updating cutscene")
UpdateCutscene_osi(p4,branch, g_Log)
end
puts("Waiting.........")
sleep (14400) # seconds opt[:delay]
end
rescue => msg
g_Log.error("Crash Deteted ")
g_Log.error(msg)
if not DEBUG
send_email(EMAIL, AUTHOR, 'Auto Approved Cutscene Updater', msg)
end
end
end