Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of KZDuplicate v1.0.5
BepInEx\plugins\KZDuplicate\KZDUPLICATE.dll
Decompiled a day agousing System; using System.Collections; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using ExitGames.Client.Photon; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("KZDUPLICATE")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("KZDUPLICATE")] [assembly: AssemblyTitle("KZDUPLICATE")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } [BepInPlugin("com.yourname.kzdub", "KZDuplicate (R.E.P.O)", "1.0.5")] public class KZDUPLICATEPlugin : BaseUnityPlugin, IOnEventCallback { private ConfigEntry<KeyCode> DuplicateKey; private ConfigEntry<KeyCode> GiveCoinKey; private ConfigEntry<float> MaxDistance; private ConfigEntry<float> SpawnOffset; private const byte DuplicateEventCode = 156; private const byte CoinEventCode = 157; private bool isCallbackRegistered; private void Awake() { DuplicateKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "DuplicateKey", (KeyCode)109, "Key to duplicate grabbed item"); GiveCoinKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "GiveCoinKey", (KeyCode)110, "Key to give a random cosmetic coin to all players"); MaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxDistance", 5f, "Max distance to consider an object as grabbed"); SpawnOffset = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SpawnOffset", 0.5f, "Offset applied to duplicated object position"); } private void OnDestroy() { if (isCallbackRegistered && PhotonNetwork.NetworkingClient != null) { PhotonNetwork.RemoveCallbackTarget((object)this); } } private void Update() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (!isCallbackRegistered && PhotonNetwork.NetworkingClient != null) { PhotonNetwork.AddCallbackTarget((object)this); isCallbackRegistered = true; } if (Input.GetKeyDown(DuplicateKey.Value)) { DuplicateNearbyGrabbed(); } if (Input.GetKeyDown(GiveCoinKey.Value)) { GiveRandomCoin(); } } public void OnEvent(EventData photonEvent) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if (photonEvent.Code == 156) { try { object[] obj = (object[])photonEvent.CustomData; int num = (int)obj[0]; int[] array = (int[])obj[1]; Vector3 val = (Vector3)obj[2]; Quaternion val2 = (Quaternion)obj[3]; PhotonView val3 = PhotonView.Find(num); if ((Object)(object)val3 != (Object)null) { if (array.Length != 0 && (Object)(object)PhotonView.Find(array[0]) != (Object)null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)$"KZDuplicate: ViewID {array[0]} ya esta en uso. Abortando duplicacion para evitar kick."); } else { GameObject val4 = new GameObject("KZTempParent"); val4.SetActive(false); GameObject val5 = Object.Instantiate<GameObject>(((Component)val3).gameObject, val, val2, val4.transform); ((Object)val5).name = ((Object)((Component)val3).gameObject).name.Replace("(Clone)", "") + "_dup"; PhotonView[] componentsInChildren = val5.GetComponentsInChildren<PhotonView>(true); for (int i = 0; i < componentsInChildren.Length && i < array.Length; i++) { componentsInChildren[i].ViewID = array[i]; } ResetPhysGrabState(val5); val5.transform.SetParent((Transform)null); val5.SetActive(true); Object.Destroy((Object)(object)val4); if (PhotonNetwork.IsMasterClient) { CopyComponentState(((Component)val3).gameObject, val5, "CosmeticWorldObject"); CopyComponentState(((Component)val3).gameObject, val5, "ValuableObject"); CopyComponentState(((Component)val3).gameObject, val5, "ItemUpgrade"); SyncValuableObject(val5); } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"KZDuplicate: Received Network Duplication for '{((Object)val5).name}' with ViewID {array[0]}."); } } return; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("KZDuplicate: Error handling DuplicateEvent: " + ex)); return; } } if (photonEvent.Code == 157) { try { int rarity = (int)photonEvent.CustomData; GiveCoinLocalFallback(rarity); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)("KZDuplicate: Error handling CoinEvent: " + ex2)); } } } private void GiveRandomCoin() { //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Expected O, but got Unknown //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) try { int num = Random.Range(0, 4); if (PhotonNetwork.IsConnected && PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient) { string baseName = "Cosmetic World Object - Common"; if (num == 1) { baseName = "Cosmetic World Object - Uncommon"; } if (num == 2) { baseName = "Cosmetic World Object - Rare"; } if (num == 3) { baseName = "Cosmetic World Object - UltraRare"; } string text = FindPrefabPathInPool(baseName); try { GameObject val = PhotonNetwork.InstantiateRoomObject(text, new Vector3(0f, -1000f, 0f), Quaternion.identity, (byte)0, (object[])null); if ((Object)(object)val != (Object)null) { PhotonView component = val.GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null) { component.RPC("ExtractRPC", (RpcTarget)0, Array.Empty<object>()); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"KZDuplicate: Otorgada moneda cosmética (Rareza: {num}) vía ExtractRPC nativo."); return; } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("KZDuplicate: Native ExtractRPC failed: " + ex.Message + ". Fallback to RaiseEvent.")); } } if (PhotonNetwork.IsConnected && PhotonNetwork.InRoom) { object obj = num; RaiseEventOptions val2 = new RaiseEventOptions { Receivers = (ReceiverGroup)0 }; SendOptions val3 = default(SendOptions); ((SendOptions)(ref val3)).Reliability = true; SendOptions val4 = val3; PhotonNetwork.RaiseEvent((byte)157, obj, val2, val4); } GiveCoinLocalFallback(num); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)("KZDuplicate: Error al dar moneda cosmética: " + ex2)); } } private void GiveCoinLocalFallback(int rarity) { try { Type type = Type.GetType("RoundDirector, Assembly-CSharp"); if (type != null) { FieldInfo field = type.GetField("instance", BindingFlags.Static | BindingFlags.Public); if (field != null) { object value = field.GetValue(null); if (value != null) { MethodInfo method = type.GetMethod("CosmeticWorldObjectExtracted", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { Type type2 = Type.GetType("SemiFunc+Rarity, Assembly-CSharp") ?? Type.GetType("Rarity, Assembly-CSharp"); if (type2 != null) { object obj = Enum.ToObject(type2, rarity); method.Invoke(value, new object[1] { obj }); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"KZDuplicate: Otorgada moneda cosmética (Rareza: {rarity}) a través de RoundDirector (Fallback Local)."); return; } } } } } Type type3 = Type.GetType("PlayerCosmetics, Assembly-CSharp"); if (!(type3 != null)) { return; } Object[] array = Object.FindObjectsOfType(type3); Type type4 = Type.GetType("SemiFunc+Rarity, Assembly-CSharp") ?? Type.GetType("Rarity, Assembly-CSharp"); if (!(type4 != null)) { return; } object obj2 = Enum.ToObject(type4, rarity); Object[] array2 = array; foreach (Object obj3 in array2) { MethodInfo method2 = type3.GetMethod("CosmeticTokenAdd", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method2 != null) { method2.Invoke(obj3, new object[1] { obj2 }); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("KZDuplicate: Error en GiveCoinLocalFallback: " + ex)); } } private void DuplicateNearbyGrabbed() { //IL_054d: Unknown result type (might be due to invalid IL or missing references) //IL_0554: Expected O, but got Unknown //IL_055d: Unknown result type (might be due to invalid IL or missing references) //IL_0565: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_03e7: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Expected O, but got Unknown //IL_03f7: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: Unknown result type (might be due to invalid IL or missing references) //IL_04db: Unknown result type (might be due to invalid IL or missing references) //IL_04eb: Unknown result type (might be due to invalid IL or missing references) //IL_04ff: Unknown result type (might be due to invalid IL or missing references) //IL_0504: Unknown result type (might be due to invalid IL or missing references) //IL_0506: Unknown result type (might be due to invalid IL or missing references) //IL_050d: Unknown result type (might be due to invalid IL or missing references) //IL_051b: Unknown result type (might be due to invalid IL or missing references) //IL_0522: Expected O, but got Unknown //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return; } GameObject val = null; if ((Object)(object)PhysGrabber.instance != (Object)null && (Object)(object)PhysGrabber.instance.grabbedObjectTransform != (Object)null) { val = ((Component)PhysGrabber.instance.grabbedObjectTransform).gameObject; ((BaseUnityPlugin)this).Logger.LogInfo((object)("KZDuplicate: Detectado objeto agarrado: " + ((Object)val).name)); } RaycastHit val2 = default(RaycastHit); if ((Object)(object)val == (Object)null && Physics.Raycast(new Ray(((Component)main).transform.position, ((Component)main).transform.forward), ref val2, MaxDistance.Value, -1, (QueryTriggerInteraction)2) && (Object)(object)((RaycastHit)(ref val2)).collider != (Object)null) { val = (Object.op_Implicit((Object)(object)((RaycastHit)(ref val2)).collider.attachedRigidbody) ? ((Component)((RaycastHit)(ref val2)).collider.attachedRigidbody).gameObject : ((Component)((RaycastHit)(ref val2)).collider).gameObject); ((BaseUnityPlugin)this).Logger.LogInfo((object)("KZDuplicate: Raycast detecto: " + ((Object)val).name + (((RaycastHit)(ref val2)).collider.isTrigger ? " (Trigger)" : " (Solid)"))); } if ((Object)(object)val == (Object)null) { Collider[] array = Physics.OverlapSphere(((Component)main).transform.position + ((Component)main).transform.forward * 1f, 1f, -1, (QueryTriggerInteraction)2); foreach (Collider val3 in array) { if (!((Object)((Component)val3).gameObject).name.Contains("Player") && !((Object)((Component)val3).gameObject).name.Contains("Camera")) { val = (Object.op_Implicit((Object)(object)val3.attachedRigidbody) ? ((Component)val3.attachedRigidbody).gameObject : ((Component)val3).gameObject); if ((Object)(object)val != (Object)null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("KZDuplicate: OverlapSphere detecto: " + ((Object)val).name)); break; } } } } if ((Object)(object)val == (Object)null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"KZDuplicate: No se encontro ningun objeto para duplicar (Mira mas de cerca)."); return; } if ((Object)(object)val.GetComponent("PlayerAvatar") != (Object)null || (Object)(object)val.GetComponent<Camera>() != (Object)null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"KZDuplicate: Bloqueada duplicacion de jugador o camara por componente."); return; } string text = ((Object)val).name.ToLower(); if (text.Contains("player") && !text.Contains("item") && !text.Contains("upgrade") && !text.Contains("valuable")) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("KZDuplicate: Bloqueada duplicacion por nombre (posible jugador): " + ((Object)val).name)); return; } try { Vector3 val4 = ((Component)main).transform.position + ((Component)main).transform.forward * 1.5f; string text2 = ((Object)val).name.Replace("(Clone)", "").Replace("_dup", "").Trim(); string text3 = FindPrefabPathInPool(text2); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"KZDuplicate: Intentando duplicar '{text2}' en {val4}..."); if (PhotonNetwork.IsConnected && PhotonNetwork.InRoom) { try { GameObject val5 = PhotonNetwork.Instantiate(text3, val4, val.transform.rotation, (byte)0, (object[])null); if (PhotonNetwork.IsMasterClient && (Object)(object)val5 != (Object)null) { CopyComponentState(val, val5, "CosmeticWorldObject"); CopyComponentState(val, val5, "ValuableObject"); CopyComponentState(val, val5, "ItemUpgrade"); SyncValuableObject(val5); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"KZDuplicate: Duplicacion nativa Photon exitosa."); return; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("KZDuplicate: Photon.Instantiate fallo: " + ex.Message)); } } PhotonView component = val.GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && PhotonNetwork.IsConnected && PhotonNetwork.InRoom) { int viewID = component.ViewID; GameObject val6 = new GameObject("KZTempParent"); val6.SetActive(false); GameObject val7 = Object.Instantiate<GameObject>(val, val4, val.transform.rotation, val6.transform); ((Object)val7).name = text2 + "_dup"; PhotonView[] componentsInChildren = val7.GetComponentsInChildren<PhotonView>(true); int[] array2 = new int[componentsInChildren.Length]; for (int j = 0; j < componentsInChildren.Length; j++) { PhotonNetwork.AllocateViewID(componentsInChildren[j]); array2[j] = componentsInChildren[j].ViewID; } ResetPhysGrabState(val7); val7.transform.SetParent((Transform)null); val7.SetActive(true); Object.Destroy((Object)(object)val6); if (PhotonNetwork.IsMasterClient) { CopyComponentState(val, val7, "CosmeticWorldObject"); CopyComponentState(val, val7, "ValuableObject"); CopyComponentState(val, val7, "ItemUpgrade"); SyncValuableObject(val7); } object[] array3 = new object[4] { viewID, array2, val4, val.transform.rotation }; RaiseEventOptions val8 = new RaiseEventOptions { Receivers = (ReceiverGroup)0 }; SendOptions val9 = default(SendOptions); ((SendOptions)(ref val9)).Reliability = true; PhotonNetwork.RaiseEvent((byte)156, (object)array3, val8, val9); ((BaseUnityPlugin)this).Logger.LogInfo((object)("KZDuplicate: Fallback Network Duplicated via RaiseEvent '" + ((Object)val).name + "'.")); return; } GameObject val10 = new GameObject("KZTempParent"); val10.SetActive(false); GameObject val11 = Object.Instantiate<GameObject>(val, val4, val.transform.rotation, val10.transform); ((Object)val11).name = text2 + "_dup"; PhotonView[] componentsInChildren2 = val11.GetComponentsInChildren<PhotonView>(true); foreach (PhotonView val12 in componentsInChildren2) { if (!PhotonNetwork.AllocateViewID(val12)) { val12.ViewID = Random.Range(10000, 99999); } } ResetPhysGrabState(val11); val11.transform.SetParent((Transform)null); val11.SetActive(true); Object.Destroy((Object)(object)val10); ((BaseUnityPlugin)this).Logger.LogInfo((object)"KZDuplicate: Duplicacion local exitosa."); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)("KZDuplicate: Failed to duplicate object: " + ex2)); } } private void SyncValuableObject(GameObject go) { try { Component component = go.GetComponent("ValuableObject"); if ((Object)(object)component == (Object)null) { return; } PhotonView component2 = go.GetComponent<PhotonView>(); if (!((Object)(object)component2 == (Object)null)) { FieldInfo field = ((object)component).GetType().GetField("dollarValueCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field == null)) { float num = (float)field.GetValue(component); component2.RPC("DollarValueSetRPC", (RpcTarget)0, new object[1] { num }); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"KZDuplicate: Valor sincronizado para {((Object)go).name}: {num}"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("KZDuplicate: Error al sincronizar valor: " + ex.Message)); } } private void CopyComponentState(GameObject source, GameObject target, string componentName) { if (componentName == "ItemUpgrade") { Type type = Type.GetType("ItemUpgrade, Assembly-CSharp"); if (type != null) { Component[] components = source.GetComponents(type); Component[] components2 = target.GetComponents(type); for (int i = 0; i < components.Length && i < components2.Length; i++) { CopyFields(components[i], components2[i]); SetPrivateField(components2[i], "upgradeDone", false); SetPrivateField(components2[i], "itemActivated", false); } return; } } Component component = source.GetComponent(componentName); Component component2 = target.GetComponent(componentName); if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null) { CopyFields(component, component2); } } private void CopyFields(Component src, Component dst) { FieldInfo[] fields = ((object)src).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { try { if (!(fieldInfo.Name == "photonView") && !(fieldInfo.Name == "pv") && !(fieldInfo.Name == "discovered")) { Type fieldType = fieldInfo.FieldType; bool num = fieldType.IsValueType || fieldType == typeof(string); bool flag = typeof(ScriptableObject).IsAssignableFrom(fieldType); if (num || flag) { fieldInfo.SetValue(dst, fieldInfo.GetValue(src)); } } } catch { } } } private string FindPrefabPathInPool(string baseName) { try { IPunPrefabPool prefabPool = PhotonNetwork.PrefabPool; if (prefabPool == null) { return baseName; } if (((object)prefabPool).GetType().GetField("ResourceCache", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(prefabPool) is IDictionary dictionary) { foreach (DictionaryEntry item in dictionary) { object? value = item.Value; GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val != (Object)null && ((Object)val).name.Replace("(Clone)", "").Trim() == baseName) { return (string)item.Key; } } string[] array = new string[7] { baseName, "Items/" + baseName, "Props/" + baseName, "Prefabs/" + baseName, "Cosmetic/" + baseName, "Prefabs/Items/" + baseName, "Prefabs/Props/" + baseName }; foreach (string text in array) { GameObject val2 = Resources.Load<GameObject>(text); if ((Object)(object)val2 != (Object)null) { dictionary[text] = val2; return text; } } GameObject[] array2 = Resources.FindObjectsOfTypeAll<GameObject>(); foreach (GameObject val3 in array2) { if (((Object)val3).name == baseName) { string text2 = "KZ_" + baseName; dictionary[text2] = val3; return text2; } } } } catch { } return baseName; } private void ResetPhysGrabState(GameObject clone) { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) try { Type type = Type.GetType("PhysGrabObject, Assembly-CSharp"); if (!(type != null)) { return; } Component[] componentsInChildren = clone.GetComponentsInChildren(type, true); foreach (Component val in componentsInChildren) { SetPrivateField(val, "grabbed", false); SetPrivateField(val, "grabbedLocal", false); SetPrivateField(val, "heldByLocalPlayer", false); SetPrivateField(val, "hasNeverBeenGrabbed", true); FieldInfo field = type.GetField("playerGrabbing", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { (field.GetValue(val) as IList)?.Clear(); } Rigidbody component = val.GetComponent<Rigidbody>(); if ((Object)(object)component != (Object)null) { component.isKinematic = false; component.velocity = Vector3.zero; component.angularVelocity = Vector3.zero; } } } catch { } } private void SetPrivateField(object obj, string fieldName, object value) { FieldInfo field = obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(obj, value); } } }