mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-04 23:39:22 +00:00
[ShareBubbles] Fixes for 2025r180
This commit is contained in:
parent
13e206cd58
commit
a0a859aa86
14 changed files with 60 additions and 1953 deletions
File diff suppressed because it is too large
Load diff
|
@ -17,7 +17,7 @@ using NAK.ShareBubbles.Properties;
|
||||||
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ShareBubbles"
|
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ShareBubbles"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
|
[assembly: MelonGame("ChilloutVR", "ChilloutVR")]
|
||||||
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||||
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
||||||
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
|
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
|
||||||
|
@ -27,6 +27,6 @@ using NAK.ShareBubbles.Properties;
|
||||||
namespace NAK.ShareBubbles.Properties;
|
namespace NAK.ShareBubbles.Properties;
|
||||||
internal static class AssemblyInfoParams
|
internal static class AssemblyInfoParams
|
||||||
{
|
{
|
||||||
public const string Version = "1.0.5";
|
public const string Version = "1.1.6";
|
||||||
public const string Author = "NotAKidoS, Exterrata, Noachi, RaidShadowLily, Tejler";
|
public const string Author = "NotAKidoS, Exterrata, Noachi, RaidShadowLily, Tejler";
|
||||||
}
|
}
|
|
@ -1,66 +0,0 @@
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace NAK.ShareBubbles.API.Exceptions;
|
|
||||||
|
|
||||||
public class ShareApiException : Exception
|
|
||||||
{
|
|
||||||
public HttpStatusCode StatusCode { get; } // TODO: network back status code to claiming client, to show why the request failed
|
|
||||||
public string UserFriendlyMessage { get; }
|
|
||||||
|
|
||||||
public ShareApiException(HttpStatusCode statusCode, string message, string userFriendlyMessage)
|
|
||||||
: base(message)
|
|
||||||
{
|
|
||||||
StatusCode = statusCode;
|
|
||||||
UserFriendlyMessage = userFriendlyMessage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ContentNotSharedException : ShareApiException
|
|
||||||
{
|
|
||||||
public ContentNotSharedException(string contentId)
|
|
||||||
: base(HttpStatusCode.BadRequest,
|
|
||||||
$"Content {contentId} is not currently shared",
|
|
||||||
"This content is not currently shared with anyone")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ContentNotFoundException : ShareApiException
|
|
||||||
{
|
|
||||||
public ContentNotFoundException(string contentId)
|
|
||||||
: base(HttpStatusCode.NotFound,
|
|
||||||
$"Content {contentId} not found",
|
|
||||||
"The specified content could not be found")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UserOnlyAllowsSharesFromFriendsException : ShareApiException
|
|
||||||
{
|
|
||||||
public UserOnlyAllowsSharesFromFriendsException(string userId)
|
|
||||||
: base(HttpStatusCode.Forbidden,
|
|
||||||
$"User {userId} only accepts shares from friends",
|
|
||||||
"This user only accepts shares from friends")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UserNotFoundException : ShareApiException
|
|
||||||
{
|
|
||||||
public UserNotFoundException(string userId)
|
|
||||||
: base(HttpStatusCode.NotFound,
|
|
||||||
$"User {userId} not found",
|
|
||||||
"The specified user could not be found")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ContentAlreadySharedException : ShareApiException
|
|
||||||
{
|
|
||||||
public ContentAlreadySharedException(string contentId, string userId)
|
|
||||||
: base(HttpStatusCode.Conflict,
|
|
||||||
$"Content {contentId} is already shared with user {userId}",
|
|
||||||
"This content is already shared with this user")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
using ABI_RC.Core.Networking.API;
|
using ABI_RC.Core.Networking.API;
|
||||||
using ABI_RC.Core.Networking.API.Responses;
|
using ABI_RC.Core.Networking.API.Responses;
|
||||||
using NAK.ShareBubbles.API.Responses;
|
|
||||||
|
|
||||||
namespace NAK.ShareBubbles.API;
|
namespace NAK.ShareBubbles.API;
|
||||||
|
|
||||||
|
@ -34,6 +33,8 @@ public static class PedestalInfoBatchProcessor
|
||||||
{ PedestalType.Prop, false }
|
{ PedestalType.Prop, false }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This breaks compile accepting this change.
|
||||||
|
// ReSharper disable once ChangeFieldTypeToSystemThreadingLock
|
||||||
private static readonly object _lock = new();
|
private static readonly object _lock = new();
|
||||||
private const float BATCH_DELAY = 2f;
|
private const float BATCH_DELAY = 2f;
|
||||||
|
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace NAK.ShareBubbles.API.Responses;
|
|
||||||
|
|
||||||
public class ActiveSharesResponse
|
|
||||||
{
|
|
||||||
[JsonProperty("value")]
|
|
||||||
public List<ShareUser> Value { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Idk why not just reuse UserDetails
|
|
||||||
public class ShareUser
|
|
||||||
{
|
|
||||||
[JsonProperty("image")]
|
|
||||||
public string Image { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("id")]
|
|
||||||
public string Id { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("name")]
|
|
||||||
public string Name { get; set; }
|
|
||||||
}
|
|
|
@ -1,237 +0,0 @@
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Text;
|
|
||||||
using System.Web;
|
|
||||||
using ABI_RC.Core.Networking;
|
|
||||||
using ABI_RC.Core.Networking.API;
|
|
||||||
using ABI_RC.Core.Networking.API.Responses;
|
|
||||||
using ABI_RC.Core.Savior;
|
|
||||||
using NAK.ShareBubbles.API.Exceptions;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace NAK.ShareBubbles.API;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// API for content sharing management.
|
|
||||||
/// </summary>
|
|
||||||
public static class ShareApiHelper
|
|
||||||
{
|
|
||||||
#region Enums
|
|
||||||
|
|
||||||
public enum ShareContentType
|
|
||||||
{
|
|
||||||
Avatar,
|
|
||||||
Spawnable
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum ShareApiOperation
|
|
||||||
{
|
|
||||||
ShareAvatar,
|
|
||||||
ReleaseAvatar,
|
|
||||||
|
|
||||||
ShareSpawnable,
|
|
||||||
ReleaseSpawnable,
|
|
||||||
|
|
||||||
GetAvatarShares,
|
|
||||||
GetSpawnableShares
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Enums
|
|
||||||
|
|
||||||
#region Public API
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Shares content with a specified user.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">Type of content to share</param>
|
|
||||||
/// <param name="contentId">ID of the content</param>
|
|
||||||
/// <param name="userId">Target user ID</param>
|
|
||||||
/// <returns>Response containing share information</returns>
|
|
||||||
/// <exception cref="ShareApiException">Thrown when API request fails</exception>
|
|
||||||
/// <exception cref="UserNotFoundException">Thrown when target user is not found</exception>
|
|
||||||
/// <exception cref="ContentNotFoundException">Thrown when content is not found</exception>
|
|
||||||
/// <exception cref="UserOnlyAllowsSharesFromFriendsException">Thrown when user only accepts shares from friends</exception>
|
|
||||||
/// <exception cref="ContentAlreadySharedException">Thrown when content is already shared with user</exception>
|
|
||||||
public static Task<BaseResponse<T>> ShareContentAsync<T>(ShareContentType type, string contentId, string userId)
|
|
||||||
{
|
|
||||||
ShareApiOperation operation = type == ShareContentType.Avatar
|
|
||||||
? ShareApiOperation.ShareAvatar
|
|
||||||
: ShareApiOperation.ShareSpawnable;
|
|
||||||
|
|
||||||
ShareRequest data = new()
|
|
||||||
{
|
|
||||||
ContentId = contentId,
|
|
||||||
UserId = userId
|
|
||||||
};
|
|
||||||
|
|
||||||
return MakeApiRequestAsync<T>(operation, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases shared content from a specified user.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">Type of content to release</param>
|
|
||||||
/// <param name="contentId">ID of the content</param>
|
|
||||||
/// <param name="userId">Optional user ID. If null, releases share from self</param>
|
|
||||||
/// <returns>Response indicating success</returns>
|
|
||||||
/// <exception cref="ShareApiException">Thrown when API request fails</exception>
|
|
||||||
/// <exception cref="ContentNotSharedException">Thrown when content is not shared</exception>
|
|
||||||
/// <exception cref="ContentNotFoundException">Thrown when content is not found</exception>
|
|
||||||
/// <exception cref="UserNotFoundException">Thrown when specified user is not found</exception>
|
|
||||||
public static Task<BaseResponse<T>> ReleaseShareAsync<T>(ShareContentType type, string contentId, string userId = null)
|
|
||||||
{
|
|
||||||
ShareApiOperation operation = type == ShareContentType.Avatar
|
|
||||||
? ShareApiOperation.ReleaseAvatar
|
|
||||||
: ShareApiOperation.ReleaseSpawnable;
|
|
||||||
|
|
||||||
// If no user ID is provided, release share from self
|
|
||||||
userId ??= MetaPort.Instance.ownerId;
|
|
||||||
|
|
||||||
ShareRequest data = new()
|
|
||||||
{
|
|
||||||
ContentId = contentId,
|
|
||||||
UserId = userId
|
|
||||||
};
|
|
||||||
|
|
||||||
return MakeApiRequestAsync<T>(operation, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets all shares for specified content.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">Type of content</param>
|
|
||||||
/// <param name="contentId">ID of the content</param>
|
|
||||||
/// <returns>Response containing share information</returns>
|
|
||||||
/// <exception cref="ShareApiException">Thrown when API request fails</exception>
|
|
||||||
/// <exception cref="ContentNotFoundException">Thrown when content is not found</exception>
|
|
||||||
public static Task<BaseResponse<T>> GetSharesAsync<T>(ShareContentType type, string contentId)
|
|
||||||
{
|
|
||||||
ShareApiOperation operation = type == ShareContentType.Avatar
|
|
||||||
? ShareApiOperation.GetAvatarShares
|
|
||||||
: ShareApiOperation.GetSpawnableShares;
|
|
||||||
|
|
||||||
ShareRequest data = new() { ContentId = contentId };
|
|
||||||
return MakeApiRequestAsync<T>(operation, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Public API
|
|
||||||
|
|
||||||
#region Private Implementation
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
private record ShareRequest
|
|
||||||
{
|
|
||||||
public string ContentId { get; set; }
|
|
||||||
public string UserId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<BaseResponse<T>> MakeApiRequestAsync<T>(ShareApiOperation operation, ShareRequest data)
|
|
||||||
{
|
|
||||||
ValidateAuthenticationState();
|
|
||||||
|
|
||||||
(string endpoint, HttpMethod method) = GetApiEndpointAndMethod(operation, data);
|
|
||||||
using HttpRequestMessage request = CreateHttpRequest(endpoint, method, data);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using HttpResponseMessage response = await ApiConnection._client.SendAsync(request);
|
|
||||||
string content = await response.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
return HandleApiResponse<T>(response, content, data.ContentId, data.UserId);
|
|
||||||
}
|
|
||||||
catch (HttpRequestException ex)
|
|
||||||
{
|
|
||||||
throw new ShareApiException(
|
|
||||||
HttpStatusCode.ServiceUnavailable,
|
|
||||||
$"Failed to communicate with the server: {ex.Message}",
|
|
||||||
"Unable to connect to the server. Please check your internet connection.");
|
|
||||||
}
|
|
||||||
catch (JsonException ex)
|
|
||||||
{
|
|
||||||
throw new ShareApiException(
|
|
||||||
HttpStatusCode.UnprocessableEntity,
|
|
||||||
$"Failed to process response data: {ex.Message}",
|
|
||||||
"Server returned invalid data. Please try again later.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ValidateAuthenticationState()
|
|
||||||
{
|
|
||||||
if (!AuthManager.IsAuthenticated)
|
|
||||||
{
|
|
||||||
throw new ShareApiException(
|
|
||||||
HttpStatusCode.Unauthorized,
|
|
||||||
"User is not authenticated",
|
|
||||||
"Please log in to perform this action");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HttpRequestMessage CreateHttpRequest(string endpoint, HttpMethod method, ShareRequest data)
|
|
||||||
{
|
|
||||||
HttpRequestMessage request = new(method, endpoint);
|
|
||||||
|
|
||||||
if (method == HttpMethod.Post)
|
|
||||||
{
|
|
||||||
JObject json = JObject.FromObject(data);
|
|
||||||
request.Content = new StringContent(json.ToString(), Encoding.UTF8, "application/json");
|
|
||||||
}
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BaseResponse<T> HandleApiResponse<T>(HttpResponseMessage response, string content, string contentId, string userId)
|
|
||||||
{
|
|
||||||
if (response.IsSuccessStatusCode)
|
|
||||||
return CreateSuccessResponse<T>(content);
|
|
||||||
|
|
||||||
// Let specific exceptions propagate up to the caller
|
|
||||||
throw response.StatusCode switch
|
|
||||||
{
|
|
||||||
HttpStatusCode.BadRequest => new ContentNotSharedException(contentId),
|
|
||||||
HttpStatusCode.NotFound when userId != null => new UserNotFoundException(userId),
|
|
||||||
HttpStatusCode.NotFound => new ContentNotFoundException(contentId),
|
|
||||||
HttpStatusCode.Forbidden => new UserOnlyAllowsSharesFromFriendsException(userId),
|
|
||||||
HttpStatusCode.Conflict => new ContentAlreadySharedException(contentId, userId),
|
|
||||||
_ => new ShareApiException(
|
|
||||||
response.StatusCode,
|
|
||||||
$"API request failed with status {response.StatusCode}: {content}",
|
|
||||||
"An unexpected error occurred. Please try again later.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BaseResponse<T> CreateSuccessResponse<T>(string content)
|
|
||||||
{
|
|
||||||
var response = new BaseResponse<T>("")
|
|
||||||
{
|
|
||||||
IsSuccessStatusCode = true,
|
|
||||||
HttpStatusCode = HttpStatusCode.OK
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(content))
|
|
||||||
{
|
|
||||||
response.Data = JsonConvert.DeserializeObject<T>(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (string endpoint, HttpMethod method) GetApiEndpointAndMethod(ShareApiOperation operation, ShareRequest data)
|
|
||||||
{
|
|
||||||
string baseUrl = $"{ApiConnection.APIAddress}/{ApiConnection.APIVersion}";
|
|
||||||
string encodedContentId = HttpUtility.UrlEncode(data.ContentId);
|
|
||||||
|
|
||||||
return operation switch
|
|
||||||
{
|
|
||||||
ShareApiOperation.GetAvatarShares => ($"{baseUrl}/avatars/{encodedContentId}/shares", HttpMethod.Get),
|
|
||||||
ShareApiOperation.ShareAvatar => ($"{baseUrl}/avatars/{encodedContentId}/shares/{HttpUtility.UrlEncode(data.UserId)}", HttpMethod.Post),
|
|
||||||
ShareApiOperation.ReleaseAvatar => ($"{baseUrl}/avatars/{encodedContentId}/shares/{HttpUtility.UrlEncode(data.UserId)}", HttpMethod.Delete),
|
|
||||||
ShareApiOperation.GetSpawnableShares => ($"{baseUrl}/spawnables/{encodedContentId}/shares", HttpMethod.Get),
|
|
||||||
ShareApiOperation.ShareSpawnable => ($"{baseUrl}/spawnables/{encodedContentId}/shares/{HttpUtility.UrlEncode(data.UserId)}", HttpMethod.Post),
|
|
||||||
ShareApiOperation.ReleaseSpawnable => ($"{baseUrl}/spawnables/{encodedContentId}/shares/{HttpUtility.UrlEncode(data.UserId)}", HttpMethod.Delete),
|
|
||||||
_ => throw new ArgumentException($"Unknown operation: {operation}")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Private Implementation
|
|
||||||
}
|
|
|
@ -1,10 +1,10 @@
|
||||||
using ABI_RC.Core.EventSystem;
|
using ABI_RC.Core.EventSystem;
|
||||||
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;
|
||||||
|
using ABI_RC.Core.Networking.API.Exceptions;
|
||||||
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 ShareBubbles.ShareBubbles.Implementation;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
|
|
|
@ -1,10 +1,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;
|
||||||
|
using ABI_RC.Core.Networking.API.Exceptions;
|
||||||
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 ShareBubbles.ShareBubbles.Implementation;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using ABI_RC.Core.Networking.API.UserWebsocket;
|
using ABI_RC.Core.Networking.API;
|
||||||
|
using ABI_RC.Core.Networking.API.UserWebsocket;
|
||||||
using ABI_RC.Core.Networking.IO.Instancing;
|
using ABI_RC.Core.Networking.IO.Instancing;
|
||||||
using ABI_RC.Core.Player;
|
using ABI_RC.Core.Player;
|
||||||
using ABI_RC.Systems.GameEventSystem;
|
using ABI_RC.Systems.GameEventSystem;
|
||||||
using NAK.ShareBubbles.API;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using ABI_RC.Core.Networking.IO.Social;
|
using ABI_RC.Core.Networking.API;
|
||||||
|
using ABI_RC.Core.Networking.IO.Social;
|
||||||
using ABI_RC.Core.Savior;
|
using ABI_RC.Core.Savior;
|
||||||
using ABI_RC.Systems.ModNetwork;
|
using ABI_RC.Systems.ModNetwork;
|
||||||
using NAK.ShareBubbles.API;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace NAK.ShareBubbles.Networking;
|
namespace NAK.ShareBubbles.Networking;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using ABI_RC.Systems.ModNetwork;
|
using ABI_RC.Core.Networking.API;
|
||||||
using NAK.ShareBubbles.API;
|
using ABI_RC.Systems.ModNetwork;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace NAK.ShareBubbles.Networking;
|
namespace NAK.ShareBubbles.Networking;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
using ABI_RC.Core.InteractionSystem;
|
using ABI_RC.Core.InteractionSystem;
|
||||||
using ABI_RC.Core.InteractionSystem.Base;
|
using ABI_RC.Core.InteractionSystem.Base;
|
||||||
using ABI_RC.Core.Player;
|
using ABI_RC.Core.Player;
|
||||||
|
using ABI_RC.Core.Savior;
|
||||||
|
using ABI_RC.Systems.InputManagement;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace NAK.ShareBubbles.UI;
|
namespace NAK.ShareBubbles.UI;
|
||||||
|
@ -9,10 +11,22 @@ namespace NAK.ShareBubbles.UI;
|
||||||
// Must be added manually by ShareBubble creation...
|
// Must be added manually by ShareBubble creation...
|
||||||
public class BubbleInteract : Interactable
|
public class BubbleInteract : Interactable
|
||||||
{
|
{
|
||||||
public override bool IsInteractableWithinRange(Vector3 sourcePos)
|
public override bool IsInteractable
|
||||||
{
|
{
|
||||||
return Vector3.Distance(transform.position, sourcePos) < 1.5f;
|
get
|
||||||
|
{
|
||||||
|
if (ViewManager.Instance.IsAnyMenuOpen)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!MetaPort.Instance.isUsingVr
|
||||||
|
&& CVRInputManager.Instance.unlockMouse)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsInteractableWithinRange(Vector3 sourcePos) => true;
|
||||||
|
|
||||||
public override void OnInteractDown(InteractionContext context, ControllerRay controllerRay)
|
public override void OnInteractDown(InteractionContext context, ControllerRay controllerRay)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"_id": 244,
|
"_id": 244,
|
||||||
"name": "ShareBubbles",
|
"name": "ShareBubbles",
|
||||||
"modversion": "1.0.5",
|
"modversion": "1.1.6",
|
||||||
"gameversion": "2025r179",
|
"gameversion": "2025r80",
|
||||||
"loaderversion": "0.6.1",
|
"loaderversion": "0.7.2",
|
||||||
"modtype": "Mod",
|
"modtype": "Mod",
|
||||||
"author": "NotAKidoS, Exterrata, Noachi, RaidShadowLily, Tejler, Luc",
|
"author": "NotAKidoS, Exterrata, Noachi, RaidShadowLily, Tejler, Luc",
|
||||||
"description": "Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network.\n### Features:\n- Can drop Share Bubbles linking to **any** Avatar or Prop page.\n - Share Bubbles can grant Permanent or Temporary shares to your **Private** Content if configured.\n- Adds basic in-game Share Management:\n - Directly share content to users within the current instance.\n - Revoke granted or received content shares.\n### Important:\nThe claiming of content shares through Share Bubbles will likely fail if you do not allow content shares from non-friends in your ABI Account settings!\n\n-# More information can be found on the [README](https://github.com/NotAKidoS/NAK_CVR_Mods/blob/main/ShareBubbles/README.md).",
|
"description": "Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network.\n### Features:\n- Can drop Share Bubbles linking to **any** Avatar or Prop page.\n - Share Bubbles can grant Permanent or Temporary shares to your **Private** Content if configured.\n- Adds basic in-game Share Management:\n - Directly share content to users within the current instance.\n - Revoke granted or received content shares.\n### Important:\nThe claiming of content shares through Share Bubbles will likely fail if you do not allow content shares from non-friends in your ABI Account settings!\n\n-# More information can be found on the [README](https://github.com/NotAKidoS/NAK_CVR_Mods/blob/main/ShareBubbles/README.md).",
|
||||||
|
@ -17,8 +17,8 @@
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"None"
|
"None"
|
||||||
],
|
],
|
||||||
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r46/ShareBubbles.dll",
|
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r47/ShareBubbles.dll",
|
||||||
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ShareBubbles/",
|
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ShareBubbles/",
|
||||||
"changelog": "- Fixes for 2025r179\n- Fixed Public/Private text on bubble not being correct\n- Fixed bubble displaying as locked or not claimed despite being shared the content if it was private and not owned by you",
|
"changelog": "- Fixes for 2025r180",
|
||||||
"embedcolor": "#f61963"
|
"embedcolor": "#f61963"
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue