Skip to content

Commit

Permalink
Simplify code through the power of publicization
Browse files Browse the repository at this point in the history
  • Loading branch information
Banane9 committed Jan 24, 2025
1 parent b418868 commit 764efa5
Showing 1 changed file with 162 additions and 170 deletions.
332 changes: 162 additions & 170 deletions CommunityBugFixCollection/DuplicateAndMoveMultipleGrabbedItems.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,82 +2,75 @@
using FrooxEngine;
using FrooxEngine.Undo;
using HarmonyLib;
using MonkeyLoader.Configuration;
using MonkeyLoader.Resonite;
using System;
using System.Collections.Generic;
using System.Text;
using static FrooxEngine.Worker;

namespace CommunityBugFixCollection
{
public static class DuplicateExtensions
internal static class DuplicateExtensions
{
// Literally just a copy paste of Slot.Duplicate but with a tiny snippet added to the middle
// would probably do this using a transpiler but it would require some kind of additional function argument to whether it should create undo steps
// or I could check stack traces to see if it's called from within my code but that's a little jank

public static Slot UndoableChildrenDuplicate(this Slot toDuplicate, Slot duplicateRoot = null, bool keepGlobalTransform = true, DuplicationSettings settings = null)
public static Slot UndoableChildrenDuplicate(this Slot toDuplicate, Slot? duplicateRoot = null, bool keepGlobalTransform = true, DuplicationSettings? settings = null)
{
if (toDuplicate.IsRootSlot)
{
throw new Exception("Cannot duplicate root slot");
}
if (duplicateRoot == null)
{
duplicateRoot = toDuplicate.Parent ?? toDuplicate.World.RootSlot;
}
else if (duplicateRoot.IsChildOf(toDuplicate))
{

duplicateRoot ??= toDuplicate.Parent ?? toDuplicate.World.RootSlot;

if (duplicateRoot.IsChildOf(toDuplicate))
throw new Exception("Target for the duplicate hierarchy cannot be within the hierarchy of the source");
}
//InternalReferences internalReferences = new InternalReferences();
var internalReferences = typeof(Worker).GetNestedType("InternalReferences", AccessTools.all).GetConstructor(new Type[] { }).Invoke(null);
HashSet<ISyncRef> hashSet = Pool.BorrowHashSet<ISyncRef>();
HashSet<Slot> hashSet2 = Pool.BorrowHashSet<Slot>();
List<Action> postDuplication = Pool.BorrowList<Action>();
toDuplicate.ForeachComponentInChildren(delegate (IDuplicationHandler h)

using var internalReferences = new InternalReferences();
var syncRefs = Pool.BorrowHashSet<ISyncRef>();
var slots = Pool.BorrowHashSet<Slot>();
var postDuplication = Pool.BorrowList<Action>();

void DuplicationHandler(IDuplicationHandler handler)
{
h.OnBeforeDuplicate(toDuplicate, out var onDuplicated);
if (onDuplicated != null)
{
handler.OnBeforeDuplicate(toDuplicate, out var onDuplicated);

if (onDuplicated is not null)
postDuplication.Add(onDuplicated);
}
}, includeLocal: false, cacheItems: true);
toDuplicate.GenerateHierarchy(hashSet2);
DuplicateFix.Debug("traverse1");
Traverse.Create(toDuplicate).Method("CollectInternalReferences", toDuplicate, internalReferences, hashSet, hashSet2).GetValue();
DuplicateFix.Debug("traverse2");
Slot slot = (Slot)typeof(Slot).GetMethod("InternalDuplicate", AccessTools.all).Invoke(toDuplicate, new object[] { duplicateRoot, internalReferences, hashSet, settings });
if (keepGlobalTransform)
{
slot.CopyTransform(toDuplicate);
}
DuplicateFix.Debug("traverse3");
Traverse.Create(internalReferences).Method("TransferReferences", false).GetValue();
List<Component> list = Pool.BorrowList<Component>();
slot.GetComponentsInChildren(list);

toDuplicate.ForeachComponentInChildren<IDuplicationHandler>(DuplicationHandler,
includeLocal: false, cacheItems: true);

toDuplicate.GenerateHierarchy(slots);
toDuplicate.CollectInternalReferences(toDuplicate, internalReferences, syncRefs, slots);
var duplicated = toDuplicate.InternalDuplicate(duplicateRoot, internalReferences, syncRefs, settings!);

if (keepGlobalTransform)
duplicated.CopyTransform(toDuplicate);

internalReferences.TransferReferences(false);

// arti stuff begin
foreach (var child in slot.Children)
{
foreach (var child in duplicated.Children)
child.CreateSpawnUndoPoint();
}
// arti stuff end
var runDuplicateMethod = typeof(Component).GetMethod("RunDuplicate", AccessTools.all);
foreach (Component item in list)
{
runDuplicateMethod.Invoke(item, null);
}
Pool.Return(ref list);
Pool.Return(ref hashSet);

DuplicateFix.Debug("traverse4");
Traverse.Create(internalReferences).Method("Dispose").GetValue();
foreach (Action item2 in postDuplication)
{
item2();
}
var duplicatedComponents = Pool.BorrowList<Component>();
duplicated.GetComponentsInChildren(duplicatedComponents);

foreach (var component in duplicatedComponents)
component.RunDuplicate();

Pool.Return(ref duplicatedComponents);
Pool.Return(ref syncRefs);

foreach (var postDuplicationAction in postDuplication)
postDuplicationAction();

Pool.Return(ref postDuplication);
return slot;

return duplicated;
}
}

Expand All @@ -88,153 +81,152 @@ internal sealed class DuplicateAndMoveMultipleGrabbedItems : ResoniteMonkey<Dupl
public override IEnumerable<string> Authors => Contributors.Art0007i;
public override bool CanBeDisabled => true;

[HarmonyPatch(typeof(InteractionHandler), "DuplicateGrabbed", new Type[] { })]
private class DuplicateFixPatch
[HarmonyPrefix]
[HarmonyPatch(typeof(InteractionHandler), "DuplicateGrabbed", [])]
public static bool DuplicateGrabbedPrefix(InteractionHandler __instance)
{
public static bool Prefix(InteractionHandler __instance)
{
if (!config.GetValue(KEY_ENABLED)) return true;
if (!Enabled)
return false;

Slot tempHolder = null;
Slot dupeHolder = null;
__instance.World.BeginUndoBatch("Undo.DuplicateGrabbed".AsLocaleKey());
try
{
tempHolder = __instance.Grabber.Slot.AddSlot("Holder");
foreach (IGrabbable grabbedObject in __instance.Grabber.GrabbedObjects)
{
if (__instance.Grabber.GrabbableGetComponentInParents<IDuplicateBlock>(grabbedObject.Slot, null, excludeDisabled: true) != null)
{
grabbedObject.Slot.SetParent(tempHolder, false);
continue;
}
}
Slot? tempHolder = null;
Slot? dupeHolder = null;

// by the time the duplication is done the children will have already escaped using their Grabbable.OnDuplicate function
// so I just copied the entire duplication sequence and made it create undo steps at the correct time.. kinda jank but works
/*dupeHolder = __instance.Grabber.HolderSlot.Duplicate();
__instance.World.BeginUndoBatch("Undo.DuplicateGrabbed".AsLocaleKey());

foreach (var child in dupeHolder.Children)
{
child.CreateSpawnUndoPoint();
}*/
dupeHolder = __instance.Grabber.HolderSlot.UndoableChildrenDuplicate();
try
{
tempHolder = __instance.Grabber.Slot.AddSlot("Holder");

dupeHolder.GetComponentsInChildren<IGrabbable>().ForEach(delegate (IGrabbable g)
{
if (g.IsGrabbed)
{
g.Release(g.Grabber);
}
});
tempHolder.Destroy(__instance.Grabber.HolderSlot, false);
}
catch (Exception ex)
foreach (var grabbedObject in __instance.Grabber.GrabbedObjects)
{
__instance.Debug.Error("Exception duplicating items!\n" + ex);
if (__instance.Grabber.GrabbableGetComponentInParents<IDuplicateBlock>(grabbedObject.Slot, excludeDisabled: true) == null)
continue;

grabbedObject.Slot.SetParent(tempHolder, false);
}
if (dupeHolder != null && !dupeHolder.IsRemoved) dupeHolder.Destroy(false);
if (tempHolder != null && !tempHolder.IsRemoved) tempHolder.Destroy(false);

__instance.World.EndUndoBatch();
// by the time the duplication is done the children will have already escaped using their Grabbable.OnDuplicate function
// so I just copied the entire duplication sequence and made it create undo steps at the correct time.. kinda jank but works
/*dupeHolder = __instance.Grabber.HolderSlot.Duplicate();
return false;
}
}
foreach (var child in dupeHolder.Children)
{
child.CreateSpawnUndoPoint();
}*/

[HarmonyPatch(typeof(Userspace), nameof(Userspace.Paste))]
private class UserspacePastePatch
{
public static bool Prefix(Userspace __instance, Job<Slot> __result, SavedGraph data, Slot source, float3 userspacePos, floatQ userspaceRot, float3 userspaceScale, World targetWorld)
{
if (!config.GetValue(KEY_ENABLED)) return true;
dupeHolder = __instance.Grabber.HolderSlot.UndoableChildrenDuplicate();

Job<Slot> task = new Job<Slot>();
World world = targetWorld ?? Engine.Current.WorldManager.FocusedWorld;
world.RunSynchronously(delegate
dupeHolder.GetComponentsInChildren<IGrabbable>().ForEach(static grabbable =>
{
Slot slot = null;
if (world.CanSpawnObjects())
{
float3 globalPosition = WorldManager.TransferPoint(userspacePos, Userspace.Current.World, world);
floatQ globalRotation = WorldManager.TransferRotation(userspaceRot, Userspace.Current.World, world);
float3 globalScale = WorldManager.TransferScale(userspaceScale, Userspace.Current.World, world);
if (source?.World == world && !source.IsDestroyed)
{
source.ActiveSelf = true;
source.GlobalPosition = globalPosition;
source.GlobalRotation = globalRotation;
source.GlobalScale = globalScale;
task.SetResultAndFinish(source);
}
else
{
slot = world.AddSlot("Paste");
slot.LoadObject(data.Root);
slot.GlobalPosition = globalPosition;
slot.GlobalRotation = globalRotation;
slot.GlobalScale = globalScale;
Traverse.Create(slot).Method("RunOnPaste").GetValue();
if (slot.Name == "Holder")
{
slot.Destroy(slot.Parent, false);
}
}
}
if (source != null && !source.IsDestroyed)
if (grabbable.IsGrabbed)
{
source.World.RunSynchronously(source.Destroy);
grabbable.Release(grabbable.Grabber);
}
task.SetResultAndFinish(slot);
});
__result = task;
return false;

tempHolder.Destroy(__instance.Grabber.HolderSlot, false);
}
catch (Exception ex)
{
__instance.Debug.Error("Exception duplicating items!\n" + ex);
}

dupeHolder!.FilterWorldElement()?.Destroy(false);
tempHolder!.FilterWorldElement()?.Destroy(false);

__instance.World.EndUndoBatch();

return false;
}

[HarmonyPrefix]
[HarmonyPatch(typeof(Grabber), nameof(Grabber.OnFocusChanged))]
private class UserspaceTransferPatch
public static bool OnFocusChangedPrefix(Grabber __instance, World.WorldFocus focus)
{
public static bool Prefix(Grabber __instance, World.WorldFocus focus)
{
if (!config.GetValue(KEY_ENABLED)) return true;
if (!Enabled)
return true;

if (!__instance.World.CanTransferObjectsOut())
{
return false;
}
__instance.BeforeUserspaceTransfer?.Invoke();
Traverse.Create(__instance).Method("CleanupGrabbed").GetValue();
if (focus != 0 || !__instance.IsHoldingObjects)
{
if (!__instance.World.CanTransferObjectsOut())
return false;

__instance.BeforeUserspaceTransfer?.Invoke();
__instance.CleanupGrabbed();

if (focus != 0 || !__instance.IsHoldingObjects)
return false;

foreach (var grabbedObject in __instance.GrabbedObjects)
{
if (grabbedObject.Slot.GetComponentInChildren(static (IItemPermissions p) => !p.CanSave) is not null)
return false;
}
foreach (IGrabbable grabbedObject in __instance.GrabbedObjects)
}

if (__instance.HolderSlot.LocalPosition != float3.Zero)
{
var holderOffset = __instance.HolderSlot.LocalPosition;
__instance.HolderSlot.LocalPosition = float3.Zero;

foreach (var child in __instance.HolderSlot.Children)
child.LocalPosition += holderOffset;
}

if (Userspace.TryTransferToUserspaceGrabber(__instance.HolderSlot, __instance.LinkingKey))
__instance.DestroyGrabbed();

return false;
}

[HarmonyPrefix]
[HarmonyPatch(typeof(Userspace), nameof(Userspace.Paste))]
public static bool PastePrefix(ref Job<Slot> __result, SavedGraph data, Slot source, float3 userspacePos, floatQ userspaceRot, float3 userspaceScale, World targetWorld)
{
if (!Enabled)
return true;

var task = new Job<Slot>();
var world = targetWorld ?? Engine.Current.WorldManager.FocusedWorld;

world.RunSynchronously(() =>
{
Slot? slot = null;

if (world.CanSpawnObjects())
{
if (grabbedObject.Slot.GetComponentInChildren((IItemPermissions p) => !p.CanSave) != null)
var globalPosition = WorldManager.TransferPoint(userspacePos, Userspace.Current.World, world);
var globalRotation = WorldManager.TransferRotation(userspaceRot, Userspace.Current.World, world);
var globalScale = WorldManager.TransferScale(userspaceScale, Userspace.Current.World, world);

if (source?.World == world && !source.IsDestroyed)
{
return false;
source.ActiveSelf = true;
source.GlobalPosition = globalPosition;
source.GlobalRotation = globalRotation;
source.GlobalScale = globalScale;
task.SetResultAndFinish(source);
}
}
float3 a = __instance.HolderSlot.LocalPosition;
float3 b = float3.Zero;
if (a != b)
{
float3 b2 = __instance.HolderSlot.LocalPosition;
__instance.HolderSlot.LocalPosition = float3.Zero;
foreach (Slot child in __instance.HolderSlot.Children)
else
{
a = child.LocalPosition;
child.LocalPosition = a + b2;
slot = world.AddSlot("Paste");
slot.LoadObject(data.Root, null!);
slot.GlobalPosition = globalPosition;
slot.GlobalRotation = globalRotation;
slot.GlobalScale = globalScale;

slot.RunOnPaste();

if (slot.Name == "Holder")
slot.Destroy(slot.Parent, false);
}
}

if (Userspace.TryTransferToUserspaceGrabber(__instance.HolderSlot, __instance.LinkingKey))
{
__instance.DestroyGrabbed();
}
return false;
}
if (source!.FilterWorldElement() is not null)
source!.World.RunSynchronously(source.Destroy);

task.SetResultAndFinish(slot!);
});

__result = task;
return false;
}
}
}

0 comments on commit 764efa5

Please sign in to comment.