-- Read-only: decode the captured battle with the client's own protobuf parser. -- This does not start battles, modify actors, replace handlers, or change saves. do local captureHash = __CAPTURE_HASH__ if _G.__gacBattleWireProbeV2Hash ~= captureHash then _G.__gacBattleWireProbeV2Hash = captureHash local function log(text) CS.UnityEngine.Debug.Log('[GAC_BATTLE_WIRE] ' .. captureHash .. ' ' .. tostring(text)) end local ok, err = pcall(function() local typeName = __TYPE_NAME__ local assemblies = CS.System.AppDomain.CurrentDomain:GetAssemblies() local messageType = nil for i = 0, assemblies.Length - 1 do messageType = assemblies[i]:GetType(typeName) if messageType ~= nil then break end end if messageType == nil then error('message type not found: ' .. typeName) end -- IL2CPP builds can expose the parser as a non-public member. -- Include both visibility levels instead of assuming a public getter. local flags = CS.System.Reflection.BindingFlags.Public + CS.System.Reflection.BindingFlags.NonPublic + CS.System.Reflection.BindingFlags.Static + CS.System.Reflection.BindingFlags.Instance local properties = messageType:GetProperties(flags) local parser, descriptor = nil, nil log('message_type=' .. tostring(messageType.FullName) .. ' properties=' .. tostring(properties.Length)) for i = 0, properties.Length - 1 do local property = properties[i] local getter = property:GetGetMethod(true) if getter ~= nil and getter.IsStatic then local propertyType = tostring(property.PropertyType.FullName) log('static_property=' .. tostring(property.Name) .. ' type=' .. propertyType) if string.find(propertyType, 'Google.Protobuf.MessageParser', 1, true) then parser = property:GetValue(nil, nil) elseif propertyType == 'Google.Protobuf.Reflection.MessageDescriptor' then descriptor = property:GetValue(nil, nil) end end end local fields = messageType:GetFields(flags) for i = 0, fields.Length - 1 do local field = fields[i] local fieldType = tostring(field.FieldType.FullName) log('member=' .. tostring(field.Name) .. ' type=' .. fieldType .. ' static=' .. tostring(field.IsStatic)) if field.IsStatic then if parser == nil and string.find(fieldType, 'Google.Protobuf.MessageParser', 1, true) then parser = field:GetValue(nil) elseif descriptor == nil and fieldType == 'Google.Protobuf.Reflection.MessageDescriptor' then descriptor = field:GetValue(nil) end end end local bytes = CS.System.Convert.FromBase64String(__PAYLOAD__) local message = nil if parser ~= nil then message = parser:ParseFrom(bytes) else -- A generated message can decode through MergeFrom even when -- its static parser metadata was stripped from reflection. message = CS.System.Activator.CreateInstance(messageType) local methods = messageType:GetMethods(flags) local merge = nil for i = 0, methods.Length - 1 do local method = methods[i] local parameters = method:GetParameters() if not method.IsStatic and parameters.Length == 1 and tostring(parameters[0].ParameterType.FullName) == 'Google.Protobuf.CodedInputStream' then merge = method break end end if merge == nil then error('no parser or CodedInputStream decoder on ' .. typeName) end local input = CS.Google.Protobuf.CodedInputStream(bytes) local arguments = CS.System.Array.CreateInstance(typeof(CS.System.Object), 1) arguments:SetValue(input, 0) merge:Invoke(message, arguments) end log('type=' .. typeName .. ' bytes=' .. tostring(bytes.Length)) log('decoded=' .. message:ToString()) if descriptor ~= nil then local fields = descriptor.Fields:InFieldNumberOrder() local iterator = fields:GetEnumerator() while iterator:MoveNext() do local field = iterator.Current local nested = field.MessageType log('field=' .. tostring(field.FieldNumber) .. ' name=' .. tostring(field.Name) .. ' type=' .. tostring(field.FieldType) .. ' nested=' .. (nested ~= nil and tostring(nested.FullName) or '')) end end end) if not ok then log('probe_error=' .. tostring(err)) end end end