Make work on Stable, Add response on claim fail

This commit is contained in:
NotAKidoS 2024-12-08 16:54:19 -06:00
parent e7b2ad4c31
commit ef2be62605
15 changed files with 268 additions and 108 deletions

View file

@ -19,11 +19,11 @@ public enum PedestalType
/// </summary> /// </summary>
public static class PedestalInfoBatchProcessor public static class PedestalInfoBatchProcessor
{ {
private static readonly Dictionary<PedestalType, Dictionary<string, TaskCompletionSource<PedestalInfoResponseButWithPublicationState>>> _pendingRequests private static readonly Dictionary<PedestalType, Dictionary<string, TaskCompletionSource<PedestalInfoResponse_ButCorrect>>> _pendingRequests
= new() = new()
{ {
{ PedestalType.Avatar, new Dictionary<string, TaskCompletionSource<PedestalInfoResponseButWithPublicationState>>() }, { PedestalType.Avatar, new Dictionary<string, TaskCompletionSource<PedestalInfoResponse_ButCorrect>>() },
{ PedestalType.Prop, new Dictionary<string, TaskCompletionSource<PedestalInfoResponseButWithPublicationState>>() } { PedestalType.Prop, new Dictionary<string, TaskCompletionSource<PedestalInfoResponse_ButCorrect>>() }
}; };
private static readonly Dictionary<PedestalType, bool> _isBatchProcessing private static readonly Dictionary<PedestalType, bool> _isBatchProcessing
@ -36,9 +36,9 @@ public static class PedestalInfoBatchProcessor
private static readonly object _lock = new(); private static readonly object _lock = new();
private const float BATCH_DELAY = 2f; private const float BATCH_DELAY = 2f;
public static Task<PedestalInfoResponseButWithPublicationState> QueuePedestalInfoRequest(PedestalType type, string contentId) public static Task<PedestalInfoResponse_ButCorrect> QueuePedestalInfoRequest(PedestalType type, string contentId)
{ {
var tcs = new TaskCompletionSource<PedestalInfoResponseButWithPublicationState>(); var tcs = new TaskCompletionSource<PedestalInfoResponse_ButCorrect>();
lock (_lock) lock (_lock)
{ {
@ -62,12 +62,12 @@ public static class PedestalInfoBatchProcessor
await Task.Delay(TimeSpan.FromSeconds(BATCH_DELAY)); await Task.Delay(TimeSpan.FromSeconds(BATCH_DELAY));
List<string> contentIds; List<string> contentIds;
Dictionary<string, TaskCompletionSource<PedestalInfoResponseButWithPublicationState>> requestBatch; Dictionary<string, TaskCompletionSource<PedestalInfoResponse_ButCorrect>> requestBatch;
lock (_lock) lock (_lock)
{ {
contentIds = _pendingRequests[type].Keys.ToList(); contentIds = _pendingRequests[type].Keys.ToList();
requestBatch = new Dictionary<string, TaskCompletionSource<PedestalInfoResponseButWithPublicationState>>(_pendingRequests[type]); requestBatch = new Dictionary<string, TaskCompletionSource<PedestalInfoResponse_ButCorrect>>(_pendingRequests[type]);
_pendingRequests[type].Clear(); _pendingRequests[type].Clear();
_isBatchProcessing[type] = false; _isBatchProcessing[type] = false;
//ShareBubblesMod.Logger.Msg($"Processing {type} pedestal info batch with {contentIds.Count} items"); //ShareBubblesMod.Logger.Msg($"Processing {type} pedestal info batch with {contentIds.Count} items");
@ -82,7 +82,7 @@ public static class PedestalInfoBatchProcessor
_ => throw new ArgumentException($"Unsupported pedestal type: {type}") _ => throw new ArgumentException($"Unsupported pedestal type: {type}")
}; };
var response = await ApiConnection.MakeRequest<IEnumerable<PedestalInfoResponseButWithPublicationState>>(operation, contentIds); var response = await ApiConnection.MakeRequest<IEnumerable<PedestalInfoResponse_ButCorrect>>(operation, contentIds);
if (response?.Data != null) if (response?.Data != null)
{ {

View file

@ -1,13 +0,0 @@
using ABI_RC.Core.Networking.API.Responses;
namespace NAK.ShareBubbles.API.Responses;
/// Same as PedestalInfoResponse, but with an additional field for publication state, if you could not tell by the name.
/// TODO: actually waiting on luc to add Published to PedestalInfoResponse
[Serializable]
public class PedestalInfoResponseButWithPublicationState : UgcResponse
{
public UserDetails User { get; set; }
public bool Permitted { get; set; }
public bool Published { get; set; }
}

View file

@ -0,0 +1,10 @@
using ABI_RC.Core.Networking.API.Responses;
namespace NAK.ShareBubbles.API.Responses;
[Serializable]
public class PedestalInfoResponse_ButCorrect : UgcResponse
{
public UserDetails User { get; set; }
public bool Published { get; set; } // Client mislabelled this as Permitted, but it's actually Published
}

View file

@ -2,7 +2,10 @@
using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.IO; using ABI_RC.Core.IO;
using ABI_RC.Core.Networking.API.UserWebsocket; using ABI_RC.Core.Networking.API.UserWebsocket;
using ABI_RC.Core.Savior;
using NAK.ShareBubbles.API; using NAK.ShareBubbles.API;
using NAK.ShareBubbles.API.Exceptions;
using ShareBubbles.ShareBubbles.Implementation;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
@ -36,8 +39,10 @@ namespace NAK.ShareBubbles.Impl
Name = infoResponse.Name, Name = infoResponse.Name,
ImageUrl = infoResponse.ImageUrl, ImageUrl = infoResponse.ImageUrl,
AuthorId = infoResponse.User.Id, AuthorId = infoResponse.User.Id,
IsPermitted = infoResponse.Permitted,
IsPublic = infoResponse.Published, IsPublic = infoResponse.Published,
// Permit access if Public, Owned, or (CANNOT DO PRIVATE & SHARED CAUSE API DOESNT GIVE)
IsPermitted = infoResponse.Published || infoResponse.User.Id == MetaPort.Instance.ownerId,
}; };
downloadedTexture = await ImageCache.GetImageAsync(details.ImageUrl); downloadedTexture = await ImageCache.GetImageAsync(details.ImageUrl);
@ -57,28 +62,40 @@ namespace NAK.ShareBubbles.Impl
}); });
} }
public void HandleClaimAccept(string userId, Action<bool> onClaimActionCompleted) public async Task<ShareClaimResult> HandleClaimAccept(string userId)
{
Task.Run(async () =>
{ {
if (details == null)
return ShareClaimResult.Rejected();
try try
{ {
var response = await ShareApiHelper.ShareContentAsync<BaseResponse>( await ShareApiHelper.ShareContentAsync<BaseResponse>(
ShareApiHelper.ShareContentType.Avatar, avatarId, userId); ShareApiHelper.ShareContentType.Avatar,
avatarId,
userId);
// Store the temporary share to revoke when either party leaves the instance // Add to temp shares if session access
if (bubble.Data.Access == ShareAccess.Session) if (bubble.Data.Access == ShareAccess.Session)
{
TempShareManager.Instance.AddTempShare(ShareApiHelper.ShareContentType.Avatar, TempShareManager.Instance.AddTempShare(ShareApiHelper.ShareContentType.Avatar,
avatarId, userId); avatarId, userId);
}
onClaimActionCompleted(response.IsSuccessStatusCode); return ShareClaimResult.Success(bubble.Data.Access == ShareAccess.Session);
}
catch (ContentAlreadySharedException)
{
return ShareClaimResult.AlreadyShared();
}
catch (UserOnlyAllowsSharesFromFriendsException)
{
return ShareClaimResult.FriendsOnly();
} }
catch (Exception ex) catch (Exception ex)
{ {
ShareBubblesMod.Logger.Error($"Error sharing avatar: {ex.Message}"); ShareBubblesMod.Logger.Error($"Error sharing avatar: {ex.Message}");
onClaimActionCompleted(false); return ShareClaimResult.Rejected();
} }
});
} }
public void ViewDetailsPage() public void ViewDetailsPage()

View file

@ -1,4 +1,6 @@
namespace NAK.ShareBubbles.Impl; using ShareBubbles.ShareBubbles.Implementation;
namespace NAK.ShareBubbles.Impl;
public interface IShareBubbleImpl public interface IShareBubbleImpl
{ {
@ -8,6 +10,6 @@ public interface IShareBubbleImpl
Task FetchContentInfo(); // Load the content info from the API Task FetchContentInfo(); // Load the content info from the API
void ViewDetailsPage(); // Open the details page for the content void ViewDetailsPage(); // Open the details page for the content
void EquipContent(); // Equip the content (Switch/Select) void EquipContent(); // Equip the content (Switch/Select)
void HandleClaimAccept(string userId, Action<bool> onClaimActionCompleted); // Handle the claim action (Share via API) Task<ShareClaimResult> HandleClaimAccept(string userId);
void Cleanup(); // Cleanup any resources void Cleanup(); // Cleanup any resources
} }

View file

@ -0,0 +1,27 @@
using NAK.ShareBubbles.Networking;
namespace ShareBubbles.ShareBubbles.Implementation;
public class ShareClaimResult
{
public ModNetwork.ClaimResponseType ResponseType { get; }
public bool RequiresSessionTracking { get; }
private ShareClaimResult(ModNetwork.ClaimResponseType responseType, bool requiresSessionTracking = false)
{
ResponseType = responseType;
RequiresSessionTracking = requiresSessionTracking;
}
public static ShareClaimResult Success(bool isSessionShare = false)
=> new(ModNetwork.ClaimResponseType.Accepted, isSessionShare);
public static ShareClaimResult AlreadyShared()
=> new(ModNetwork.ClaimResponseType.AlreadyShared);
public static ShareClaimResult FriendsOnly()
=> new(ModNetwork.ClaimResponseType.NotAcceptingSharesFromNonFriends);
public static ShareClaimResult Rejected()
=> new(ModNetwork.ClaimResponseType.Rejected);
}

View file

@ -2,7 +2,10 @@
using ABI_RC.Core.IO; using ABI_RC.Core.IO;
using ABI_RC.Core.Networking.API.UserWebsocket; using ABI_RC.Core.Networking.API.UserWebsocket;
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using NAK.ShareBubbles.API; using NAK.ShareBubbles.API;
using NAK.ShareBubbles.API.Exceptions;
using ShareBubbles.ShareBubbles.Implementation;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
@ -36,8 +39,10 @@ namespace NAK.ShareBubbles.Impl
Name = infoResponse.Name, Name = infoResponse.Name,
ImageUrl = infoResponse.ImageUrl, ImageUrl = infoResponse.ImageUrl,
AuthorId = infoResponse.User.Id, AuthorId = infoResponse.User.Id,
IsPermitted = infoResponse.Permitted,
IsPublic = infoResponse.Published, IsPublic = infoResponse.Published,
// Permit access if Public, Owned, or (CANNOT DO PRIVATE & SHARED CAUSE API DOESNT GIVE)
IsPermitted = infoResponse.Published || infoResponse.User.Id == MetaPort.Instance.ownerId,
}; };
downloadedTexture = await ImageCache.GetImageAsync(details.ImageUrl); downloadedTexture = await ImageCache.GetImageAsync(details.ImageUrl);
@ -57,36 +62,40 @@ namespace NAK.ShareBubbles.Impl
}); });
} }
public void HandleClaimAccept(string userId, Action<bool> onClaimActionCompleted) public async Task<ShareClaimResult> HandleClaimAccept(string userId)
{ {
if (details == null) if (details == null)
{ return ShareClaimResult.Rejected();
onClaimActionCompleted(false);
return;
}
Task.Run(async () =>
{
try try
{ {
var response = await ShareApiHelper.ShareContentAsync<BaseResponse>( await ShareApiHelper.ShareContentAsync<BaseResponse>(
ShareApiHelper.ShareContentType.Spawnable, ShareApiHelper.ShareContentType.Spawnable,
spawnableId, spawnableId,
userId); userId);
// Store the temporary share to revoke when either party leaves the instance // Add to temp shares if session access
if (bubble.Data.Access == ShareAccess.Session) if (bubble.Data.Access == ShareAccess.Session)
{
TempShareManager.Instance.AddTempShare(ShareApiHelper.ShareContentType.Spawnable, TempShareManager.Instance.AddTempShare(ShareApiHelper.ShareContentType.Spawnable,
spawnableId, userId); spawnableId, userId);
}
onClaimActionCompleted(response.IsSuccessStatusCode); return ShareClaimResult.Success(bubble.Data.Access == ShareAccess.Session);
}
catch (ContentAlreadySharedException)
{
return ShareClaimResult.AlreadyShared();
}
catch (UserOnlyAllowsSharesFromFriendsException)
{
return ShareClaimResult.FriendsOnly();
} }
catch (Exception ex) catch (Exception ex)
{ {
ShareBubblesMod.Logger.Error($"Error sharing spawnable: {ex.Message}"); ShareBubblesMod.Logger.Error($"Error sharing spawnable: {ex.Message}");
onClaimActionCompleted(false); return ShareClaimResult.Rejected();
} }
});
} }
public void ViewDetailsPage() public void ViewDetailsPage()

View file

@ -7,5 +7,7 @@ public static partial class ModNetwork
private const string NetworkVersion = "1.0.1"; // change each time network protocol changes private const string NetworkVersion = "1.0.1"; // change each time network protocol changes
private const string ModId = $"NAK.SB:{NetworkVersion}"; // Cannot exceed 32 characters private const string ModId = $"NAK.SB:{NetworkVersion}"; // Cannot exceed 32 characters
private const float ClaimRequestTimeout = 30f; // 30 second timeout
#endregion Constants #endregion Constants
} }

View file

@ -29,9 +29,18 @@ public static partial class ModNetwork
private enum MNLogLevel : byte private enum MNLogLevel : byte
{ {
Info = 0, Info,
Warning = 1, Warning,
Error = 2 Error
}
public enum ClaimResponseType : byte
{
Accepted,
Rejected,
NotAcceptingSharesFromNonFriends,
AlreadyShared,
Timeout
} }
#endregion Enums #endregion Enums

View file

@ -150,11 +150,20 @@ public static partial class ModNetwork
private static void HandleBubbleClaimResponse(ModNetworkMessage msg) private static void HandleBubbleClaimResponse(ModNetworkMessage msg)
{ {
msg.Read(out uint bubbleNetworkId); msg.Read(out uint bubbleNetworkId);
msg.Read(out bool claimAccepted); msg.Read(out byte responseTypeRaw);
ShareBubbleManager.Instance.OnRemoteBubbleClaimResponse(msg.Sender, bubbleNetworkId, claimAccepted); if (!Enum.IsDefined(typeof(ClaimResponseType), responseTypeRaw))
{
LoggerInbound($"Invalid claim response type received: {responseTypeRaw}");
return;
}
LoggerInbound($"Bubble with ID {bubbleNetworkId} claim response: {claimAccepted}"); ClaimResponseType responseType = (ClaimResponseType)responseTypeRaw;
if (_pendingClaimRequests.TryGetValue(bubbleNetworkId, out PendingClaimRequest request))
request.CompletionSource.TrySetResult(responseType);
LoggerInbound($"Bubble with ID {bubbleNetworkId} claim response: {responseType}");
} }
private static void HandleActiveBubblesRequest(ModNetworkMessage msg) private static void HandleActiveBubblesRequest(ModNetworkMessage msg)

View file

@ -4,6 +4,7 @@ namespace NAK.ShareBubbles.Networking;
public static partial class ModNetwork public static partial class ModNetwork
{ {
#region Mod Network Internals #region Mod Network Internals
private static bool _isSubscribedToModNetwork; private static bool _isSubscribedToModNetwork;
@ -33,4 +34,24 @@ public static partial class ModNetwork
} }
#endregion Mod Network Internals #endregion Mod Network Internals
#region Pending Claim Requests
private static readonly Dictionary<uint, PendingClaimRequest> _pendingClaimRequests = new();
public class PendingClaimRequest
{
public TaskCompletionSource<ClaimResponseType> CompletionSource { get; }
public DateTime RequestTime { get; }
public uint BubbleId { get; }
public PendingClaimRequest(uint bubbleId)
{
CompletionSource = new TaskCompletionSource<ClaimResponseType>();
RequestTime = DateTime.UtcNow;
BubbleId = bubbleId;
}
}
#endregion
} }

View file

@ -51,20 +51,44 @@ public static partial class ModNetwork
LoggerOutbound($"Sending BubbleMove message for bubble {bubbleId}"); LoggerOutbound($"Sending BubbleMove message for bubble {bubbleId}");
} }
public static void SendBubbleClaimRequest(string bubbleOwnerId, uint bubbleNetworkId) public static async Task<ClaimResponseType> SendBubbleClaimRequestAsync(string bubbleOwnerId, uint bubbleNetworkId)
{ {
if (!CanSendModNetworkMessage()) if (!CanSendModNetworkMessage())
return; return ClaimResponseType.Rejected;
using ModNetworkMessage modMsg = new(ModId, bubbleOwnerId); // Create pending request
PendingClaimRequest request = new(bubbleNetworkId);
_pendingClaimRequests[bubbleNetworkId] = request;
// Send request
using (ModNetworkMessage modMsg = new(ModId, bubbleOwnerId))
{
modMsg.Write((byte)MessageType.BubbleClaimRequest); modMsg.Write((byte)MessageType.BubbleClaimRequest);
modMsg.Write(bubbleNetworkId); modMsg.Write(bubbleNetworkId);
modMsg.Send(); modMsg.Send();
LoggerOutbound($"Sending BubbleClaimRequest message for bubble {bubbleNetworkId}");
} }
public static void SendBubbleClaimResponse(string requesterUserId, uint bubbleNetworkId, bool success) LoggerOutbound($"Sending BubbleClaimRequest message for bubble {bubbleNetworkId}");
try
{
// Wait for response with timeout
using CancellationTokenSource cts = new(TimeSpan.FromSeconds(ClaimRequestTimeout));
Task timeoutTask = Task.Delay(TimeSpan.FromSeconds(ClaimRequestTimeout), cts.Token);
var responseTask = request.CompletionSource.Task;
Task completedTask = await Task.WhenAny(responseTask, timeoutTask);
if (completedTask == timeoutTask) return ClaimResponseType.Timeout;
return await responseTask;
}
finally
{
_pendingClaimRequests.Remove(bubbleNetworkId);
}
}
public static void SendBubbleClaimResponse(string requesterUserId, uint bubbleNetworkId, ClaimResponseType responseType)
{ {
if (!CanSendModNetworkMessage()) if (!CanSendModNetworkMessage())
return; return;
@ -72,10 +96,10 @@ public static partial class ModNetwork
using ModNetworkMessage modMsg = new(ModId, requesterUserId); using ModNetworkMessage modMsg = new(ModId, requesterUserId);
modMsg.Write((byte)MessageType.BubbleClaimResponse); modMsg.Write((byte)MessageType.BubbleClaimResponse);
modMsg.Write(bubbleNetworkId); modMsg.Write(bubbleNetworkId);
modMsg.Write(success); modMsg.Write((byte)responseType);
modMsg.Send(); modMsg.Send();
LoggerOutbound($"Sending BubbleClaimResponse message for bubble {bubbleNetworkId}"); LoggerOutbound($"Sending BubbleClaimResponse message for bubble {bubbleNetworkId}: {responseType}");
} }
public static void SendActiveBubblesRequest() public static void SendActiveBubblesRequest()

View file

@ -8,6 +8,8 @@ using NAK.ShareBubbles.Networking;
using NAK.ShareBubbles.UI; using NAK.ShareBubbles.UI;
using TMPro; using TMPro;
using System.Collections; using System.Collections;
using ABI_RC.Core.InteractionSystem;
using ShareBubbles.ShareBubbles.Implementation;
namespace NAK.ShareBubbles; namespace NAK.ShareBubbles;
@ -308,18 +310,62 @@ public class ShareBubble : MonoBehaviour
public void RequestContentClaim() public void RequestContentClaim()
{ {
if (!CanRequestClaim()) return; if (!CanRequestClaim()) return;
if (!RequestClaimTimeoutInactive()) return;
lastClaimRequest = DateTime.Now; // Fire and forget but with error handling~
ModNetwork.SendBubbleClaimRequest(OwnerId, Data.BubbleId); _ = RequestContentClaimAsync().ContinueWith(task =>
{
if (!task.IsFaulted)
return;
ShareBubblesMod.Logger.Error($"Error requesting content claim: {task.Exception}");
CurrentClaimState = ClaimState.Rejected;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
private async Task<ModNetwork.ClaimResponseType> RequestContentClaimAsync()
{
if (!CanRequestClaim())
return ModNetwork.ClaimResponseType.Rejected;
CurrentClaimState = ClaimState.Requested; CurrentClaimState = ClaimState.Requested;
return; try
bool RequestClaimTimeoutInactive()
{ {
if (!lastClaimRequest.HasValue) return true; ModNetwork.ClaimResponseType response = await ModNetwork.SendBubbleClaimRequestAsync(OwnerId, Data.BubbleId);
TimeSpan timeSinceLastRequest = DateTime.Now - lastClaimRequest.Value;
return timeSinceLastRequest.TotalSeconds >= ClaimTimeout; switch (response)
{
case ModNetwork.ClaimResponseType.Accepted:
case ModNetwork.ClaimResponseType.AlreadyShared:
CurrentClaimState = ClaimState.Permitted;
break;
default:
case ModNetwork.ClaimResponseType.Rejected:
case ModNetwork.ClaimResponseType.Timeout:
CurrentClaimState = ClaimState.Rejected;
break;
case ModNetwork.ClaimResponseType.NotAcceptingSharesFromNonFriends:
CurrentClaimState = ClaimState.Rejected;
string ownerName = CVRPlayerManager.Instance.TryGetPlayerName(OwnerId);
ShareBubblesMod.Logger.Msg($"Claim request for {Data.BubbleId} rejected: " +
$"You are not friends with the owner ({ownerName}) and do not have the permission " +
$"enabled on the Community Hub to accept shares from non-friends.");
// Display in UI
ViewManager.Instance.TriggerAlert("Claim Request Rejected",
$"You are not friends with the owner ({ownerName}) and do not have the permission " +
"enabled on the Community Hub to accept shares from non-friends.", -1, false);
break;
}
return response;
}
catch
{
CurrentClaimState = ClaimState.Rejected;
return ModNetwork.ClaimResponseType.Rejected;
} }
} }
@ -341,22 +387,19 @@ public class ShareBubble : MonoBehaviour
UpdateButtonStates(); UpdateButtonStates();
} }
public void OnRemoteWantsClaim(string requesterId) public async void OnRemoteWantsClaim(string requesterId)
{ {
if (!IsOwnBubble) return; if (!IsOwnBubble) return;
// Check if requester is allowed to claim // Rule check bypass attempt - reject immediately
bool isAllowed = Data.Rule == ShareRule.Everyone || if (Data.Rule == ShareRule.FriendsOnly && !Friends.FriendsWith(requesterId))
(Data.Rule == ShareRule.FriendsOnly && Friends.FriendsWith(requesterId));
if (!isAllowed)
{ {
ModNetwork.SendBubbleClaimResponse(requesterId, Data.BubbleId, false); ModNetwork.SendBubbleClaimResponse(requesterId, Data.BubbleId, ModNetwork.ClaimResponseType.Rejected);
return; return;
} }
implementation.HandleClaimAccept(requesterId, ShareClaimResult result = await implementation.HandleClaimAccept(requesterId);
wasAccepted => ModNetwork.SendBubbleClaimResponse(requesterId, Data.BubbleId, wasAccepted)); ModNetwork.SendBubbleClaimResponse(requesterId, Data.BubbleId, result.ResponseType);
} }
#endregion Mod Network Callbacks #endregion Mod Network Callbacks

View file

@ -14,12 +14,12 @@ public class BubbleInteract : Interactable
return Vector3.Distance(transform.position, sourcePos) < 1.5f; return Vector3.Distance(transform.position, sourcePos) < 1.5f;
} }
public override void OnInteractDown(InteractionContext context, ControllerRay controllerRay) public override void OnInteractDown(ControllerRay controllerRay)
{ {
// Not used // Not used
} }
public override void OnInteractUp(InteractionContext context, ControllerRay controllerRay) public override void OnInteractUp(ControllerRay controllerRay)
{ {
if (PlayerSetup.Instance.GetCurrentPropSelectionMode() if (PlayerSetup.Instance.GetCurrentPropSelectionMode()
!= PlayerSetup.PropSelectionMode.None) != PlayerSetup.PropSelectionMode.None)
@ -39,12 +39,12 @@ public class BubbleInteract : Interactable
GetComponentInParent<ShareBubble>().ViewDetailsPage(); GetComponentInParent<ShareBubble>().ViewDetailsPage();
} }
public override void OnHoverEnter(InteractionContext context, ControllerRay controllerRay) public override void OnHoverEnter()
{ {
// Not used // Not used
} }
public override void OnHoverExit(InteractionContext context, ControllerRay controllerRay) public override void OnHoverExit()
{ {
// Not used // Not used
} }

View file

@ -30,12 +30,12 @@ public class ReturnOnRelease : MonoBehaviour
pickupable.onDrop.AddListener(OnPickupRelease); pickupable.onDrop.AddListener(OnPickupRelease);
} }
public void OnPickupGrabbed(InteractionContext _) public void OnPickupGrabbed()
{ {
isReturning = false; isReturning = false;
} }
public void OnPickupRelease(InteractionContext _) public void OnPickupRelease()
{ {
isReturning = true; isReturning = true;
} }