Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,11 @@ protected override void OnBeforeTransmit()
if (Body == null) return;
Vector3 p = Body.position;
Quaternion r = Body.rotation;
if (RelativeTo != null) p = RelativeTo.InverseTransformPoint(p);
if (RelativeTo != null)
{
p = RelativeTo.InverseTransformPoint(p);
r = Quaternion.Inverse(RelativeTo.rotation) * r;
}

if (_posX.IsValid) LocalSet(_posX, p.x);
if (_posY.IsValid) LocalSet(_posY, p.y);
Expand Down Expand Up @@ -209,11 +213,12 @@ protected override void ApplyInterpolated()

Vector3 p = Body.position;
Quaternion r = Body.rotation;
bool hasSyncedPosition = _posX.IsValid;
bool hasSyncedRotation = _rotQuat.IsValid || _rotQw.IsValid || _rotEuler;

if (_posX.IsValid) p.x = GetFloat(_posX);
if (_posY.IsValid) p.y = GetFloat(_posY);
if (_posZ.IsValid) p.z = GetFloat(_posZ);
if (RelativeTo != null && _posX.IsValid) p = RelativeTo.TransformPoint(p);

if (_rotQuat.IsValid)
{
Expand All @@ -234,6 +239,12 @@ protected override void ApplyInterpolated()
r = Quaternion.Euler(GetAngle(_eulerX), GetAngle(_eulerY), GetAngle(_eulerZ));
}

if (RelativeTo != null)
{
if (hasSyncedPosition) p = RelativeTo.TransformPoint(p);
if (hasSyncedRotation) r = RelativeTo.rotation * r;
}

if (DriveRemoteKinematic)
{
if (!Body.isKinematic) Body.isKinematic = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ protected virtual void Awake()
if (RotationX || RotationY || RotationZ)
{
_rotEuler = true;
_heldEuler = WorldSpace ? Target.eulerAngles : Target.localEulerAngles;
_heldEuler = RelativeTo != null
? (Quaternion.Inverse(RelativeTo.rotation) * Target.rotation).eulerAngles
: WorldSpace ? Target.eulerAngles : Target.localEulerAngles;
if (RotationX) _eulerX = RegisterAngle(RotCompX.ToSpec(), InterpolateRotation);
if (RotationY) _eulerY = RegisterAngle(RotCompY.ToSpec(), InterpolateRotation);
if (RotationZ) _eulerZ = RegisterAngle(RotCompZ.ToSpec(), InterpolateRotation);
Expand Down Expand Up @@ -188,9 +190,14 @@ protected override void OnBeforeTransmit()

Vector3 p;
Quaternion r;
if (WorldSpace) Target.GetPositionAndRotation(out p, out r);
if (RelativeTo != null)
{
Target.GetPositionAndRotation(out p, out r);
p = RelativeTo.InverseTransformPoint(p);
r = Quaternion.Inverse(RelativeTo.rotation) * r;
}
else if (WorldSpace) Target.GetPositionAndRotation(out p, out r);
else Target.GetLocalPositionAndRotation(out p, out r);
if (RelativeTo != null) p = RelativeTo.InverseTransformPoint(Target.position);

if (_posX.IsValid) LocalSet(_posX, p.x);
if (_posY.IsValid) LocalSet(_posY, p.y);
Expand All @@ -209,7 +216,7 @@ protected override void OnBeforeTransmit()
}
else if (_rotEuler)
{
Vector3 e = WorldSpace ? Target.eulerAngles : Target.localEulerAngles;
Vector3 e = r.eulerAngles;
if (_eulerX.IsValid) LocalSet(_eulerX, e.x);
if (_eulerY.IsValid) LocalSet(_eulerY, e.y);
if (_eulerZ.IsValid) LocalSet(_eulerZ, e.z);
Expand Down Expand Up @@ -279,9 +286,7 @@ protected override void ApplyInterpolated()

if (RelativeTo != null)
{
Target.position = RelativeTo.TransformPoint(p);
if (WorldSpace) Target.rotation = r;
else Target.localRotation = r;
Target.SetPositionAndRotation(RelativeTo.TransformPoint(p), RelativeTo.rotation * r);
}
else if (WorldSpace) Target.SetPositionAndRotation(p, r);
else Target.SetLocalPositionAndRotation(p, r);
Expand All @@ -300,10 +305,15 @@ protected bool ComposeSyncedPose(out Vector3 p, out Quaternion r, out Vector3 s)
s = Vector3.one;
if (Target == null) return false;

if (WorldSpace) Target.GetPositionAndRotation(out p, out r);
if (RelativeTo != null)
{
Target.GetPositionAndRotation(out p, out r);
p = RelativeTo.InverseTransformPoint(p);
r = Quaternion.Inverse(RelativeTo.rotation) * r;
}
else if (WorldSpace) Target.GetPositionAndRotation(out p, out r);
else Target.GetLocalPositionAndRotation(out p, out r);
s = Target.localScale;
if (RelativeTo != null) p = RelativeTo.InverseTransformPoint(Target.position);

if (_posX.IsValid) p.x = GetFloat(_posX);
if (_posY.IsValid) p.y = GetFloat(_posY);
Expand Down Expand Up @@ -354,15 +364,22 @@ protected override bool TryGetSyncGizmoSpatial(BasisSyncValues from, BasisSyncVa

// Unsynced axes have no keyframe data — hold them at the Target's live value so the
// from/to points sit on the real motion path.
Vector3 baseLocal = WorldSpace ? Target.position : Target.localPosition;
Vector3 baseLocal = RelativeTo != null
? RelativeTo.InverseTransformPoint(Target.position)
: WorldSpace ? Target.position : Target.localPosition;
Vector3 f = baseLocal;
Vector3 t = baseLocal;
if (_posX.IsValid) { int o = Schema.GetField(_posX.FieldIndex).Offset; f.x = from.Cont[o]; t.x = to.Cont[o]; }
if (_posY.IsValid) { int o = Schema.GetField(_posY.FieldIndex).Offset; f.y = from.Cont[o]; t.y = to.Cont[o]; }
if (_posZ.IsValid) { int o = Schema.GetField(_posZ.FieldIndex).Offset; f.z = from.Cont[o]; t.z = to.Cont[o]; }

Transform parent = Target.parent;
if (WorldSpace || parent == null)
if (RelativeTo != null)
{
fromWorld = RelativeTo.TransformPoint(f);
toWorld = RelativeTo.TransformPoint(t);
}
else if (WorldSpace || parent == null)
{
fromWorld = f;
toWorld = t;
Expand Down
56 changes: 49 additions & 7 deletions Basis/Packages/com.basis.imagepickup/BasisDesktopImageDropHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,25 @@ public class BasisDesktopImageDropHook : MonoBehaviour
private delegate bool EnumThreadWindowProc(IntPtr hWnd, IntPtr lParam);

[DllImport("kernel32.dll")] private static extern uint GetCurrentThreadId();
[DllImport("kernel32.dll")] private static extern void SetLastError(uint dwErrCode);
[DllImport("user32.dll")] private static extern bool EnumThreadWindows(uint threadId, EnumThreadWindowProc callback, IntPtr lParam);
[DllImport("user32.dll")] private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int index, IntPtr newLong);
[DllImport("user32.dll")] private static extern IntPtr CallWindowProc(IntPtr previous, IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")] private static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("shell32.dll")] private static extern void DragAcceptFiles(IntPtr hWnd, bool accept);
[DllImport("shell32.dll", CharSet = CharSet.Auto)] private static extern uint DragQueryFile(IntPtr hDrop, uint file, StringBuilder buffer, uint length);
[DllImport("shell32.dll")] private static extern void DragFinish(IntPtr hDrop);

private static BasisDesktopImageDropHook _instance;
private static readonly WndProcDelegate _wndProcDelegate = HookedWndProc;
private static IntPtr _foundWindow;
private static IntPtr _activePreviousWndProc = IntPtr.Zero;
private static IntPtr _activeWindowHandle = IntPtr.Zero;

private IntPtr _windowHandle = IntPtr.Zero;
private IntPtr _previousWndProc = IntPtr.Zero;
private bool _hookInstalled;

private readonly List<string> _pending = new();
private readonly object _pendingLock = new();
Expand All @@ -50,19 +55,42 @@ private void OnEnable()
return;
}

DragAcceptFiles(_windowHandle, true);
SetLastError(0);
_previousWndProc = SetWindowLongPtr(_windowHandle, GWLP_WNDPROC, Marshal.GetFunctionPointerForDelegate(_wndProcDelegate));
int error = Marshal.GetLastWin32Error();
if (_previousWndProc == IntPtr.Zero && error != 0)
{
BasisDebug.LogWarning($"Image pickup: failed to subclass the player window ({error}); file drop disabled.");
_windowHandle = IntPtr.Zero;
if (_instance == this) _instance = null;
enabled = false;
return;
}

_hookInstalled = true;
_activeWindowHandle = _windowHandle;
_activePreviousWndProc = _previousWndProc;
DragAcceptFiles(_windowHandle, true);
}

private void OnDisable()
{
if (_windowHandle != IntPtr.Zero && _previousWndProc != IntPtr.Zero)
if (_windowHandle != IntPtr.Zero)
{
SetWindowLongPtr(_windowHandle, GWLP_WNDPROC, _previousWndProc);
if (_hookInstalled)
{
SetWindowLongPtr(_windowHandle, GWLP_WNDPROC, _previousWndProc);
}
DragAcceptFiles(_windowHandle, false);
}
if (_activeWindowHandle == _windowHandle)
{
_activeWindowHandle = IntPtr.Zero;
_activePreviousWndProc = IntPtr.Zero;
}
_windowHandle = IntPtr.Zero;
_previousWndProc = IntPtr.Zero;
_hookInstalled = false;
if (_instance == this) _instance = null;
}

Expand All @@ -84,13 +112,27 @@ private void Update()
private static IntPtr HookedWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
BasisDesktopImageDropHook instance = _instance;
if (instance == null) return IntPtr.Zero;
IntPtr previous = instance != null ? instance._previousWndProc : _activePreviousWndProc;

if (msg == WM_DROPFILES)
if (instance != null && msg == WM_DROPFILES)
{
instance.CollectDroppedFiles(wParam);
try
{
instance.CollectDroppedFiles(wParam);
}
catch (Exception e)
{
BasisDebug.LogWarning($"Image pickup: file drop handling failed ({e.Message}).");
}
}
return CallWindowProc(instance._previousWndProc, hWnd, msg, wParam, lParam);
return ForwardWindowMessage(previous, hWnd, msg, wParam, lParam);
}

private static IntPtr ForwardWindowMessage(IntPtr previous, IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
return previous != IntPtr.Zero
? CallWindowProc(previous, hWnd, msg, wParam, lParam)
: DefWindowProc(hWnd, msg, wParam, lParam);
}

private void CollectDroppedFiles(IntPtr hDrop)
Expand Down
62 changes: 55 additions & 7 deletions Basis/Packages/com.basis.imagepickup/BasisImagePickupManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class BasisImagePickupManager : BasisNetworkBehaviour
public static BasisImagePickupManager Instance { get; private set; }

private const string FixedNetworkIdentifier = "BasisImagePickupManager";
private const int MaxIgnoredOwnerNameBytes = 1024;

private const byte OpSpawn = 1;
private const byte OpChunk = 2;
Expand Down Expand Up @@ -238,8 +239,8 @@ public override void OnDirectNetworkMessage(ushort senderId, byte[] buffer, Deli
private void HandleSpawn(ushort senderId, BinaryReader reader)
{
Guid id = new Guid(reader.ReadBytes(16));
ushort ownerId = reader.ReadUInt16();
string ownerName = reader.ReadString();
reader.ReadUInt16();
if (!TrySkipWireString(reader, MaxIgnoredOwnerNameBytes)) return;
int width = reader.ReadInt32();
int height = reader.ReadInt32();
int totalBytes = reader.ReadInt32();
Expand All @@ -266,8 +267,8 @@ private void HandleSpawn(ushort senderId, BinaryReader reader)
TotalChunks = totalChunks,
Width = width,
Height = height,
OwnerId = ownerId,
OwnerName = ownerName,
OwnerId = senderId,
OwnerName = ResolveOwnerName(senderId),
Deadline = Time.unscaledTime + BasisImagePickupSettings.InboundTransferTimeoutSeconds,
Position = position,
Rotation = rotation,
Expand All @@ -279,15 +280,23 @@ private void HandleChunk(ushort senderId, BinaryReader reader)
Guid id = new Guid(reader.ReadBytes(16));
int chunkIndex = reader.ReadInt32();
int length = reader.ReadInt32();
byte[] data = reader.ReadBytes(length);

if (!_inbound.TryGetValue(id, out InboundTransfer transfer)) return;
if (transfer.Sender != senderId) return;
if (chunkIndex < 0 || chunkIndex >= transfer.TotalChunks) return;
if (data.Length != length) return;
if (length < 0 || length > BasisImagePickupSettings.ChunkPayloadBytes) return;

int offset = chunkIndex * BasisImagePickupSettings.ChunkPayloadBytes;
if (offset < 0 || offset + length > transfer.Buffer.Length) return;
if (offset < 0 || offset >= transfer.Buffer.Length) return;

int expectedLength = Mathf.Min(BasisImagePickupSettings.ChunkPayloadBytes, transfer.Buffer.Length - offset);
if (length != expectedLength) return;

long remainingBytes = reader.BaseStream.Length - reader.BaseStream.Position;
if (remainingBytes < length) return;

byte[] data = reader.ReadBytes(length);
if (data.Length != length) return;

if (!transfer.Received[chunkIndex])
{
Expand Down Expand Up @@ -395,6 +404,45 @@ private void IncrementSenderCount(ushort sender)
_imageCountBySender[sender] = count + 1;
}

private static string ResolveOwnerName(ushort senderId)
{
if (BasisNetworkPlayer.GetPlayerById(senderId, out BasisNetworkPlayer player))
{
string name = player.SafeDisplayName;
if (string.IsNullOrEmpty(name)) name = player.displayName;
if (!string.IsNullOrEmpty(name)) return name;
}
return $"Player {senderId}";
}

private static bool TrySkipWireString(BinaryReader reader, int maxByteLength)
{
if (!TryRead7BitEncodedInt(reader, out int byteLength)) return false;
if (byteLength < 0 || byteLength > maxByteLength) return false;

long remainingBytes = reader.BaseStream.Length - reader.BaseStream.Position;
if (remainingBytes < byteLength) return false;

reader.BaseStream.Position += byteLength;
return true;
}

private static bool TryRead7BitEncodedInt(BinaryReader reader, out int value)
{
value = 0;
for (int shift = 0; shift < 35; shift += 7)
{
if (reader.BaseStream.Length - reader.BaseStream.Position < 1) return false;

byte b = reader.ReadByte();
if (shift == 28 && (b & 0xF0) != 0) return false;

value |= (b & 0x7F) << shift;
if ((b & 0x80) == 0) return true;
}
return false;
}

private void DecrementSenderCount(ushort sender)
{
if (_imageCountBySender.TryGetValue(sender, out int count))
Expand Down
2 changes: 1 addition & 1 deletion Basis/Packages/com.basis.imagepickup/BasisImageSecurity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public static BasisImageValidationResult ValidateBytes(byte[] bytes)
if (bytes.Length > BasisImagePickupSettings.MaxImageBytes) { result.Error = "Too large"; return result; }
if (!TryReadPngDimensions(bytes, out int w, out int h, out string headerError)) { result.Error = headerError; return result; }
if (!DimensionsWithinCaps(w, h, out string capError)) { result.Error = capError; return result; }
return BuildFromBytes(bytes, false, false);
return BuildFromBytes(bytes, true, false);
}

private static BasisImageValidationResult BuildFromBytes(byte[] bytes, bool reencode, bool allowDownscale)
Expand Down
Loading