mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2026-01-01 06:07:30 +00:00
Normalize JSON encoding
This commit is contained in:
parent
ab46b2505f
commit
5046c2259d
8 changed files with 862 additions and 861 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
# Auto detect text files and perform LF normalization
|
# Auto detect text files and perform LF normalization
|
||||||
* text=auto
|
* text=auto
|
||||||
|
*.json text eol=lf
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"_id": -1,
|
"_id": -1,
|
||||||
"name": "ShowPlayerInSelfMirror",
|
"name": "ShowPlayerInSelfMirror",
|
||||||
"modversion": "1.0.0",
|
"modversion": "1.0.0",
|
||||||
|
|
|
||||||
|
|
@ -1,122 +1,122 @@
|
||||||
using BTKUILib;
|
using BTKUILib;
|
||||||
using BTKUILib.UIObjects;
|
using BTKUILib.UIObjects;
|
||||||
using BTKUILib.UIObjects.Components;
|
using BTKUILib.UIObjects.Components;
|
||||||
using BTKUILib.UIObjects.Objects;
|
using BTKUILib.UIObjects.Objects;
|
||||||
|
|
||||||
namespace NAK.Stickers.Integrations;
|
namespace NAK.Stickers.Integrations;
|
||||||
|
|
||||||
public static partial class BTKUIAddon
|
public static partial class BTKUIAddon
|
||||||
{
|
{
|
||||||
private static Category _ourCategory;
|
private static Category _ourCategory;
|
||||||
|
|
||||||
private static Button _placeStickersButton;
|
private static Button _placeStickersButton;
|
||||||
|
|
||||||
private static readonly MultiSelection _sfxSelection =
|
private static readonly MultiSelection _sfxSelection =
|
||||||
MultiSelection.CreateMultiSelectionFromMelonPref(ModSettings.Entry_SelectedSFX);
|
MultiSelection.CreateMultiSelectionFromMelonPref(ModSettings.Entry_SelectedSFX);
|
||||||
|
|
||||||
private static readonly MultiSelection _desktopKeybindSelection =
|
private static readonly MultiSelection _desktopKeybindSelection =
|
||||||
MultiSelection.CreateMultiSelectionFromMelonPref(ModSettings.Entry_PlaceBinding);
|
MultiSelection.CreateMultiSelectionFromMelonPref(ModSettings.Entry_PlaceBinding);
|
||||||
|
|
||||||
private static readonly MultiSelection _tabDoubleClickSelection =
|
private static readonly MultiSelection _tabDoubleClickSelection =
|
||||||
MultiSelection.CreateMultiSelectionFromMelonPref(ModSettings.Entry_TabDoubleClick);
|
MultiSelection.CreateMultiSelectionFromMelonPref(ModSettings.Entry_TabDoubleClick);
|
||||||
|
|
||||||
#region Category Setup
|
#region Category Setup
|
||||||
|
|
||||||
private static void Setup_StickersModCategory()
|
private static void Setup_StickersModCategory()
|
||||||
{
|
{
|
||||||
_ourCategory = _rootPage.AddMelonCategory(ModSettings.Hidden_Foldout_SettingsCategory);
|
_ourCategory = _rootPage.AddMelonCategory(ModSettings.Hidden_Foldout_SettingsCategory);
|
||||||
|
|
||||||
_placeStickersButton = _ourCategory.AddButton("Place Stickers", "Stickers-magic-wand", "Place stickers via raycast.", ButtonStyle.TextWithIcon);
|
_placeStickersButton = _ourCategory.AddButton("Place Stickers", "Stickers-magic-wand", "Place stickers via raycast.", ButtonStyle.TextWithIcon);
|
||||||
_placeStickersButton.OnPress += OnPlaceStickersButtonClick;
|
_placeStickersButton.OnPress += OnPlaceStickersButtonClick;
|
||||||
|
|
||||||
Button clearSelfStickersButton = _ourCategory.AddButton("Clear Self", "Stickers-eraser", "Clear own stickers.", ButtonStyle.TextWithIcon);
|
Button clearSelfStickersButton = _ourCategory.AddButton("Clear Self", "Stickers-eraser", "Clear own stickers.", ButtonStyle.TextWithIcon);
|
||||||
clearSelfStickersButton.OnPress += OnClearSelfStickersButtonClick;
|
clearSelfStickersButton.OnPress += OnClearSelfStickersButtonClick;
|
||||||
|
|
||||||
Button clearAllStickersButton = _ourCategory.AddButton("Clear All", "Stickers-rubbish-bin", "Clear all stickers.", ButtonStyle.TextWithIcon);
|
Button clearAllStickersButton = _ourCategory.AddButton("Clear All", "Stickers-rubbish-bin", "Clear all stickers.", ButtonStyle.TextWithIcon);
|
||||||
clearAllStickersButton.OnPress += OnClearAllStickersButtonClick;
|
clearAllStickersButton.OnPress += OnClearAllStickersButtonClick;
|
||||||
|
|
||||||
Button openStickersFolderButton = _ourCategory.AddButton("Open Stickers Folder", "Stickers-folder", "Open UserData/Stickers folder in explorer. If above 256kb your image will automatically be downscaled for networking reasons.", ButtonStyle.TextWithIcon);
|
Button openStickersFolderButton = _ourCategory.AddButton("Open Stickers Folder", "Stickers-folder", "Open UserData/Stickers folder in explorer. If above 256kb your image will automatically be downscaled for networking reasons.", ButtonStyle.TextWithIcon);
|
||||||
openStickersFolderButton.OnPress += OnOpenStickersFolderButtonClick;
|
openStickersFolderButton.OnPress += OnOpenStickersFolderButtonClick;
|
||||||
|
|
||||||
Button openStickerSFXButton = _ourCategory.AddButton("Sticker SFX", "Stickers-headset", "Choose the SFX used when a sticker is placed.", ButtonStyle.TextWithIcon);
|
Button openStickerSFXButton = _ourCategory.AddButton("Sticker SFX", "Stickers-headset", "Choose the SFX used when a sticker is placed.", ButtonStyle.TextWithIcon);
|
||||||
openStickerSFXButton.OnPress += () => QuickMenuAPI.OpenMultiSelect(_sfxSelection);
|
openStickerSFXButton.OnPress += () => QuickMenuAPI.OpenMultiSelect(_sfxSelection);
|
||||||
|
|
||||||
ToggleButton toggleDesktopKeybindButton = _ourCategory.AddToggle("Use Desktop Keybind", "Should the Desktop keybind be active.", ModSettings.Entry_UsePlaceBinding.Value);
|
ToggleButton toggleDesktopKeybindButton = _ourCategory.AddToggle("Use Desktop Keybind", "Should the Desktop keybind be active.", ModSettings.Entry_UsePlaceBinding.Value);
|
||||||
Button openDesktopKeybindButton = _ourCategory.AddButton("Desktop Keybind", "Stickers-alphabet", "Choose the key binding to place stickers.", ButtonStyle.TextWithIcon);
|
Button openDesktopKeybindButton = _ourCategory.AddButton("Desktop Keybind", "Stickers-alphabet", "Choose the key binding to place stickers.", ButtonStyle.TextWithIcon);
|
||||||
openDesktopKeybindButton.OnPress += () => QuickMenuAPI.OpenMultiSelect(_desktopKeybindSelection);
|
openDesktopKeybindButton.OnPress += () => QuickMenuAPI.OpenMultiSelect(_desktopKeybindSelection);
|
||||||
toggleDesktopKeybindButton.OnValueUpdated += (b) =>
|
toggleDesktopKeybindButton.OnValueUpdated += (b) =>
|
||||||
{
|
{
|
||||||
ModSettings.Entry_UsePlaceBinding.Value = b;
|
ModSettings.Entry_UsePlaceBinding.Value = b;
|
||||||
openDesktopKeybindButton.Disabled = !b;
|
openDesktopKeybindButton.Disabled = !b;
|
||||||
};
|
};
|
||||||
|
|
||||||
Button openTabDoubleClickButton = _ourCategory.AddButton("Tab Double Click", "Stickers-mouse", "Choose the action to perform when double clicking the Stickers tab.", ButtonStyle.TextWithIcon);
|
Button openTabDoubleClickButton = _ourCategory.AddButton("Tab Double Click", "Stickers-mouse", "Choose the action to perform when double clicking the Stickers tab.", ButtonStyle.TextWithIcon);
|
||||||
openTabDoubleClickButton.OnPress += () => QuickMenuAPI.OpenMultiSelect(_tabDoubleClickSelection);
|
openTabDoubleClickButton.OnPress += () => QuickMenuAPI.OpenMultiSelect(_tabDoubleClickSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Category Setup
|
#endregion Category Setup
|
||||||
|
|
||||||
#region Button Actions
|
#region Button Actions
|
||||||
|
|
||||||
private static void OnPlaceStickersButtonClick()
|
private static void OnPlaceStickersButtonClick()
|
||||||
{
|
{
|
||||||
if (!_isOurTabOpened) return;
|
if (!_isOurTabOpened) return;
|
||||||
|
|
||||||
if (StickerSystem.Instance.IsRestrictedInstance)
|
if (StickerSystem.Instance.IsRestrictedInstance)
|
||||||
{
|
{
|
||||||
QuickMenuAPI.ShowAlertToast("Stickers are not allowed in this world!", 2);
|
QuickMenuAPI.ShowAlertToast("Stickers are not allowed in this world!", 2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string mode = StickerSystem.Instance.IsInStickerMode ? "Exiting" : "Entering";
|
string mode = StickerSystem.Instance.IsInStickerMode ? "Exiting" : "Entering";
|
||||||
QuickMenuAPI.ShowAlertToast($"{mode} sticker placement mode...", 2);
|
QuickMenuAPI.ShowAlertToast($"{mode} sticker placement mode...", 2);
|
||||||
StickerSystem.Instance.IsInStickerMode = !StickerSystem.Instance.IsInStickerMode;
|
StickerSystem.Instance.IsInStickerMode = !StickerSystem.Instance.IsInStickerMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnClearSelfStickersButtonClick()
|
private static void OnClearSelfStickersButtonClick()
|
||||||
{
|
{
|
||||||
if (!_isOurTabOpened) return;
|
if (!_isOurTabOpened) return;
|
||||||
QuickMenuAPI.ShowAlertToast("Clearing own stickers in world...", 2);
|
QuickMenuAPI.ShowAlertToast("Clearing own stickers in world...", 2);
|
||||||
StickerSystem.Instance.ClearStickersSelf();
|
StickerSystem.Instance.ClearStickersSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnClearAllStickersButtonClick()
|
private static void OnClearAllStickersButtonClick()
|
||||||
{
|
{
|
||||||
if (!_isOurTabOpened) return;
|
if (!_isOurTabOpened) return;
|
||||||
QuickMenuAPI.ShowAlertToast("Clearing all stickers in world...", 2);
|
QuickMenuAPI.ShowAlertToast("Clearing all stickers in world...", 2);
|
||||||
StickerSystem.Instance.ClearAllStickers();
|
StickerSystem.Instance.ClearAllStickers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnOpenStickersFolderButtonClick()
|
private static void OnOpenStickersFolderButtonClick()
|
||||||
{
|
{
|
||||||
if (!_isOurTabOpened) return;
|
if (!_isOurTabOpened) return;
|
||||||
QuickMenuAPI.ShowAlertToast("Opening Stickers folder in Explorer...", 2);
|
QuickMenuAPI.ShowAlertToast("Opening Stickers folder in Explorer...", 2);
|
||||||
StickerSystem.OpenStickersFolder();
|
StickerSystem.OpenStickersFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnStickerRestrictionUpdated(bool isRestricted = false) //TODO: add Icon changing, Bono needs to expose the value first.
|
public static void OnStickerRestrictionUpdated(bool isRestricted = false) //TODO: add Icon changing, Bono needs to expose the value first.
|
||||||
{
|
{
|
||||||
if (_rootPage == null || _placeStickersButton == null)
|
if (_rootPage == null || _placeStickersButton == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (isRestricted)
|
if (isRestricted)
|
||||||
{
|
{
|
||||||
_rootPage.MenuSubtitle = "Stickers... are sadly disabled in this world.";
|
_rootPage.MenuSubtitle = "Stickers... are sadly disabled in this world.";
|
||||||
|
|
||||||
_placeStickersButton.Disabled = true;
|
_placeStickersButton.Disabled = true;
|
||||||
_placeStickersButton.ButtonText = "Stickers Disabled";
|
_placeStickersButton.ButtonText = "Stickers Disabled";
|
||||||
_placeStickersButton.ButtonTooltip = "This world is not allowing Stickers.";
|
_placeStickersButton.ButtonTooltip = "This world is not allowing Stickers.";
|
||||||
_placeStickersButton.ButtonIcon = "Stickers-magic-wand-broken";
|
_placeStickersButton.ButtonIcon = "Stickers-magic-wand-broken";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_rootPage.MenuSubtitle = "Stickers! Double-click the tab to quickly toggle Sticker Mode.";
|
_rootPage.MenuSubtitle = "Stickers! Double-click the tab to quickly toggle Sticker Mode.";
|
||||||
|
|
||||||
_placeStickersButton.Disabled = false;
|
_placeStickersButton.Disabled = false;
|
||||||
_placeStickersButton.ButtonText = "Place Stickers";
|
_placeStickersButton.ButtonText = "Place Stickers";
|
||||||
_placeStickersButton.ButtonTooltip = "Place stickers via raycast.";
|
_placeStickersButton.ButtonTooltip = "Place stickers via raycast.";
|
||||||
_placeStickersButton.ButtonIcon = "Stickers-magic-wand";
|
_placeStickersButton.ButtonIcon = "Stickers-magic-wand";
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Button Actions
|
#endregion Button Actions
|
||||||
}
|
}
|
||||||
|
|
@ -1,116 +1,116 @@
|
||||||
using BTKUILib;
|
using BTKUILib;
|
||||||
using BTKUILib.UIObjects;
|
using BTKUILib.UIObjects;
|
||||||
using NAK.Stickers.Networking;
|
using NAK.Stickers.Networking;
|
||||||
using NAK.Stickers.Utilities;
|
using NAK.Stickers.Utilities;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace NAK.Stickers.Integrations;
|
namespace NAK.Stickers.Integrations;
|
||||||
|
|
||||||
public static partial class BTKUIAddon
|
public static partial class BTKUIAddon
|
||||||
{
|
{
|
||||||
private static Page _rootPage;
|
private static Page _rootPage;
|
||||||
private static string _rootPageElementID;
|
private static string _rootPageElementID;
|
||||||
|
|
||||||
private static bool _isOurTabOpened;
|
private static bool _isOurTabOpened;
|
||||||
|
|
||||||
public static void Initialize()
|
public static void Initialize()
|
||||||
{
|
{
|
||||||
Setup_Icons();
|
Setup_Icons();
|
||||||
Setup_StickerModTab();
|
Setup_StickerModTab();
|
||||||
Setup_PlayerOptionsPage();
|
Setup_PlayerOptionsPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Setup
|
#region Setup
|
||||||
|
|
||||||
private static void Setup_Icons()
|
private static void Setup_Icons()
|
||||||
{
|
{
|
||||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||||
string assemblyName = assembly.GetName().Name;
|
string assemblyName = assembly.GetName().Name;
|
||||||
|
|
||||||
// All icons used - https://www.flaticon.com/authors/gohsantosadrive
|
// All icons used - https://www.flaticon.com/authors/gohsantosadrive
|
||||||
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-alphabet", GetIconStream("Gohsantosadrive_Icons.Stickers-alphabet.png"));
|
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-alphabet", GetIconStream("Gohsantosadrive_Icons.Stickers-alphabet.png"));
|
||||||
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-eraser", GetIconStream("Gohsantosadrive_Icons.Stickers-eraser.png"));
|
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-eraser", GetIconStream("Gohsantosadrive_Icons.Stickers-eraser.png"));
|
||||||
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-folder", GetIconStream("Gohsantosadrive_Icons.Stickers-folder.png"));
|
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-folder", GetIconStream("Gohsantosadrive_Icons.Stickers-folder.png"));
|
||||||
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-headset", GetIconStream("Gohsantosadrive_Icons.Stickers-headset.png"));
|
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-headset", GetIconStream("Gohsantosadrive_Icons.Stickers-headset.png"));
|
||||||
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-magnifying-glass", GetIconStream("Gohsantosadrive_Icons.Stickers-magnifying-glass.png"));
|
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-magnifying-glass", GetIconStream("Gohsantosadrive_Icons.Stickers-magnifying-glass.png"));
|
||||||
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-magic-wand", GetIconStream("Gohsantosadrive_Icons.Stickers-magic-wand.png"));
|
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-magic-wand", GetIconStream("Gohsantosadrive_Icons.Stickers-magic-wand.png"));
|
||||||
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-magic-wand-broken", GetIconStream("Gohsantosadrive_Icons.Stickers-magic-wand-broken.png"));
|
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-magic-wand-broken", GetIconStream("Gohsantosadrive_Icons.Stickers-magic-wand-broken.png"));
|
||||||
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-mouse", GetIconStream("Gohsantosadrive_Icons.Stickers-mouse.png"));
|
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-mouse", GetIconStream("Gohsantosadrive_Icons.Stickers-mouse.png"));
|
||||||
//QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-pencil", GetIconStream("Gohsantosadrive_Icons.Stickers-pencil.png"));
|
//QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-pencil", GetIconStream("Gohsantosadrive_Icons.Stickers-pencil.png"));
|
||||||
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-puzzle", GetIconStream("Gohsantosadrive_Icons.Stickers-puzzle.png"));
|
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-puzzle", GetIconStream("Gohsantosadrive_Icons.Stickers-puzzle.png"));
|
||||||
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-puzzle-disabled", GetIconStream("Gohsantosadrive_Icons.Stickers-puzzle-disabled.png")); // disabled Sticker Puzzle
|
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-puzzle-disabled", GetIconStream("Gohsantosadrive_Icons.Stickers-puzzle-disabled.png")); // disabled Sticker Puzzle
|
||||||
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-rubbish-bin", GetIconStream("Gohsantosadrive_Icons.Stickers-rubbish-bin.png"));
|
QuickMenuAPI.PrepareIcon(ModSettings.ModName, "Stickers-rubbish-bin", GetIconStream("Gohsantosadrive_Icons.Stickers-rubbish-bin.png"));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
Stream GetIconStream(string iconName) => assembly.GetManifestResourceStream($"{assemblyName}.Resources.{iconName}");
|
Stream GetIconStream(string iconName) => assembly.GetManifestResourceStream($"{assemblyName}.Resources.{iconName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Setup_StickerModTab()
|
private static void Setup_StickerModTab()
|
||||||
{
|
{
|
||||||
_rootPage = new Page(ModSettings.ModName, ModSettings.SM_SettingsCategory, true, "Stickers-Puzzle") // sticker icon will be left blank as it is updated on world join, AFTER Icon value is exposed..
|
_rootPage = new Page(ModSettings.ModName, ModSettings.SM_SettingsCategory, true, "Stickers-Puzzle") // sticker icon will be left blank as it is updated on world join, AFTER Icon value is exposed..
|
||||||
{
|
{
|
||||||
MenuTitle = ModSettings.SM_SettingsCategory + $" (Network Version v{ModNetwork.NetworkVersion})",
|
MenuTitle = ModSettings.SM_SettingsCategory + $" (Network Version v{ModNetwork.NetworkVersion})",
|
||||||
MenuSubtitle = "", // left this blank as it is defined when the world loads
|
MenuSubtitle = "", // left this blank as it is defined when the world loads
|
||||||
};
|
};
|
||||||
|
|
||||||
_rootPageElementID = _rootPage.ElementID;
|
_rootPageElementID = _rootPage.ElementID;
|
||||||
|
|
||||||
QuickMenuAPI.OnTabChange += OnTabChange;
|
QuickMenuAPI.OnTabChange += OnTabChange;
|
||||||
ModNetwork.OnTextureOutboundStateChanged += (isSending) =>
|
ModNetwork.OnTextureOutboundStateChanged += (isSending) =>
|
||||||
{
|
{
|
||||||
if (_isOurTabOpened && isSending) QuickMenuAPI.ShowAlertToast("Sending Sticker over Mod Network...", 2);
|
if (_isOurTabOpened && isSending) QuickMenuAPI.ShowAlertToast("Sending Sticker over Mod Network...", 2);
|
||||||
//_rootPage.Disabled = isSending; // TODO: fix being able to select stickers while sending
|
//_rootPage.Disabled = isSending; // TODO: fix being able to select stickers while sending
|
||||||
};
|
};
|
||||||
|
|
||||||
StickerSystem.OnStickerLoaded += (slotIndex, imageRelativePath) =>
|
StickerSystem.OnStickerLoaded += (slotIndex, imageRelativePath) =>
|
||||||
{
|
{
|
||||||
if (_isOurTabOpened) QuickMenuAPI.ShowAlertToast($"Sticker loaded: {imageRelativePath}", 2);
|
if (_isOurTabOpened) QuickMenuAPI.ShowAlertToast($"Sticker loaded: {imageRelativePath}", 2);
|
||||||
_stickerSelectionButtons[slotIndex].ButtonIcon = StickerCache.GetBtkUiIconName(imageRelativePath);
|
_stickerSelectionButtons[slotIndex].ButtonIcon = StickerCache.GetBtkUiIconName(imageRelativePath);
|
||||||
};
|
};
|
||||||
|
|
||||||
StickerSystem.OnStickerLoadFailed += (slotIndex, error) =>
|
StickerSystem.OnStickerLoadFailed += (slotIndex, error) =>
|
||||||
{
|
{
|
||||||
if (_isOurTabOpened) QuickMenuAPI.ShowAlertToast(error, 3);
|
if (_isOurTabOpened) QuickMenuAPI.ShowAlertToast(error, 3);
|
||||||
};
|
};
|
||||||
|
|
||||||
Setup_StickersModCategory();
|
Setup_StickersModCategory();
|
||||||
Setup_StickerSelectionCategory();
|
Setup_StickerSelectionCategory();
|
||||||
Setup_OtherOptionsCategory();
|
Setup_OtherOptionsCategory();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Setup
|
#endregion Setup
|
||||||
|
|
||||||
#region Double-Click Place Sticker
|
#region Double-Click Place Sticker
|
||||||
|
|
||||||
private static DateTime lastTime = DateTime.Now;
|
private static DateTime lastTime = DateTime.Now;
|
||||||
|
|
||||||
private static void OnTabChange(string newTab, string previousTab)
|
private static void OnTabChange(string newTab, string previousTab)
|
||||||
{
|
{
|
||||||
_isOurTabOpened = newTab == _rootPageElementID;
|
_isOurTabOpened = newTab == _rootPageElementID;
|
||||||
if (!_isOurTabOpened) return;
|
if (!_isOurTabOpened) return;
|
||||||
|
|
||||||
TimeSpan timeDifference = DateTime.Now - lastTime;
|
TimeSpan timeDifference = DateTime.Now - lastTime;
|
||||||
if (timeDifference.TotalSeconds <= 0.5)
|
if (timeDifference.TotalSeconds <= 0.5)
|
||||||
{
|
{
|
||||||
switch (ModSettings.Entry_TabDoubleClick.Value)
|
switch (ModSettings.Entry_TabDoubleClick.Value)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case TabDoubleClick.ToggleStickerMode:
|
case TabDoubleClick.ToggleStickerMode:
|
||||||
OnPlaceStickersButtonClick();
|
OnPlaceStickersButtonClick();
|
||||||
break;
|
break;
|
||||||
case TabDoubleClick.ClearAllStickers:
|
case TabDoubleClick.ClearAllStickers:
|
||||||
OnClearAllStickersButtonClick();
|
OnClearAllStickersButtonClick();
|
||||||
break;
|
break;
|
||||||
case TabDoubleClick.ClearSelfStickers:
|
case TabDoubleClick.ClearSelfStickers:
|
||||||
OnClearSelfStickersButtonClick();
|
OnClearSelfStickersButtonClick();
|
||||||
break;
|
break;
|
||||||
case TabDoubleClick.None:
|
case TabDoubleClick.None:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lastTime = DateTime.Now;
|
lastTime = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Double-Click Place Sticker
|
#endregion Double-Click Place Sticker
|
||||||
}
|
}
|
||||||
|
|
@ -1,32 +1,32 @@
|
||||||
using NAK.Stickers.Properties;
|
using NAK.Stickers.Properties;
|
||||||
using MelonLoader;
|
using MelonLoader;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
|
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
|
||||||
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
|
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
|
||||||
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
|
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
|
||||||
[assembly: AssemblyTitle(nameof(NAK.Stickers))]
|
[assembly: AssemblyTitle(nameof(NAK.Stickers))]
|
||||||
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
|
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
|
||||||
[assembly: AssemblyProduct(nameof(NAK.Stickers))]
|
[assembly: AssemblyProduct(nameof(NAK.Stickers))]
|
||||||
|
|
||||||
[assembly: MelonInfo(
|
[assembly: MelonInfo(
|
||||||
typeof(NAK.Stickers.StickerMod),
|
typeof(NAK.Stickers.StickerMod),
|
||||||
nameof(NAK.Stickers),
|
nameof(NAK.Stickers),
|
||||||
AssemblyInfoParams.Version,
|
AssemblyInfoParams.Version,
|
||||||
AssemblyInfoParams.Author,
|
AssemblyInfoParams.Author,
|
||||||
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/Stickers"
|
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/Stickers"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
[assembly: MelonGame("ChilloutVR", "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
|
||||||
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
|
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
|
||||||
[assembly: HarmonyDontPatchAll]
|
[assembly: HarmonyDontPatchAll]
|
||||||
|
|
||||||
namespace NAK.Stickers.Properties;
|
namespace NAK.Stickers.Properties;
|
||||||
internal static class AssemblyInfoParams
|
internal static class AssemblyInfoParams
|
||||||
{
|
{
|
||||||
public const string Version = "1.1.1";
|
public const string Version = "1.1.1";
|
||||||
public const string Author = "NotAKidoS";
|
public const string Author = "NotAKidoS";
|
||||||
}
|
}
|
||||||
|
|
@ -1,260 +1,260 @@
|
||||||
using ABI_RC.Core.Networking.IO.Social;
|
using ABI_RC.Core.Networking.IO.Social;
|
||||||
using ABI_RC.Core.Player;
|
using ABI_RC.Core.Player;
|
||||||
using ABI_RC.Core.Savior;
|
using ABI_RC.Core.Savior;
|
||||||
using ABI_RC.Systems.ModNetwork;
|
using ABI_RC.Systems.ModNetwork;
|
||||||
using NAK.Stickers.Utilities;
|
using NAK.Stickers.Utilities;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace NAK.Stickers.Networking;
|
namespace NAK.Stickers.Networking;
|
||||||
|
|
||||||
public static partial class ModNetwork
|
public static partial class ModNetwork
|
||||||
{
|
{
|
||||||
#region Inbound Buffers
|
#region Inbound Buffers
|
||||||
|
|
||||||
private static readonly Dictionary<string, byte[]> _textureChunkBuffers = new();
|
private static readonly Dictionary<string, byte[]> _textureChunkBuffers = new();
|
||||||
private static readonly Dictionary<string, int> _receivedChunkCounts = new();
|
private static readonly Dictionary<string, int> _receivedChunkCounts = new();
|
||||||
private static readonly Dictionary<string, int> _expectedChunkCounts = new();
|
private static readonly Dictionary<string, int> _expectedChunkCounts = new();
|
||||||
private static readonly Dictionary<string, (int stickerSlot, Guid Hash, int Width, int Height)> _textureMetadata = new();
|
private static readonly Dictionary<string, (int stickerSlot, Guid Hash, int Width, int Height)> _textureMetadata = new();
|
||||||
|
|
||||||
#endregion Inbound Buffers
|
#endregion Inbound Buffers
|
||||||
|
|
||||||
#region Reset Method
|
#region Reset Method
|
||||||
|
|
||||||
public static void Reset()
|
public static void Reset()
|
||||||
{
|
{
|
||||||
_textureChunkBuffers.Clear();
|
_textureChunkBuffers.Clear();
|
||||||
_receivedChunkCounts.Clear();
|
_receivedChunkCounts.Clear();
|
||||||
_expectedChunkCounts.Clear();
|
_expectedChunkCounts.Clear();
|
||||||
_textureMetadata.Clear();
|
_textureMetadata.Clear();
|
||||||
|
|
||||||
LoggerInbound("ModNetwork inbound buffers and metadata have been reset.");
|
LoggerInbound("ModNetwork inbound buffers and metadata have been reset.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Reset Method
|
#endregion Reset Method
|
||||||
|
|
||||||
#region Inbound Methods
|
#region Inbound Methods
|
||||||
|
|
||||||
private static bool ShouldReceiveFromSender(string sender)
|
private static bool ShouldReceiveFromSender(string sender)
|
||||||
{
|
{
|
||||||
if (_disallowedForSession.Contains(sender))
|
if (_disallowedForSession.Contains(sender))
|
||||||
return false; // ignore messages from disallowed users
|
return false; // ignore messages from disallowed users
|
||||||
|
|
||||||
if (MetaPort.Instance.blockedUserIds.Contains(sender))
|
if (MetaPort.Instance.blockedUserIds.Contains(sender))
|
||||||
return false; // ignore messages from blocked users
|
return false; // ignore messages from blocked users
|
||||||
|
|
||||||
if (ModSettings.Entry_FriendsOnly.Value && !Friends.FriendsWith(sender))
|
if (ModSettings.Entry_FriendsOnly.Value && !Friends.FriendsWith(sender))
|
||||||
return false; // ignore messages from non-friends if friends only is enabled
|
return false; // ignore messages from non-friends if friends only is enabled
|
||||||
|
|
||||||
if (StickerSystem.Instance.IsRestrictedInstance) // ignore messages from users when the world is restricted. This also includes older or modified version of Stickers mod.
|
if (StickerSystem.Instance.IsRestrictedInstance) // ignore messages from users when the world is restricted. This also includes older or modified version of Stickers mod.
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!CVRPlayerManager.Instance.GetPlayerPuppetMaster(sender, out PuppetMaster _))
|
if (!CVRPlayerManager.Instance.GetPlayerPuppetMaster(sender, out PuppetMaster _))
|
||||||
return false; // ignore messages from players that don't exist
|
return false; // ignore messages from players that don't exist
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandleMessageReceived(ModNetworkMessage msg)
|
private static void HandleMessageReceived(ModNetworkMessage msg)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string sender = msg.Sender;
|
string sender = msg.Sender;
|
||||||
msg.Read(out byte msgTypeRaw);
|
msg.Read(out byte msgTypeRaw);
|
||||||
|
|
||||||
if (!Enum.IsDefined(typeof(MessageType), msgTypeRaw))
|
if (!Enum.IsDefined(typeof(MessageType), msgTypeRaw))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!ShouldReceiveFromSender(sender))
|
if (!ShouldReceiveFromSender(sender))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LoggerInbound($"Received message from {msg.Sender}, Type: {(MessageType)msgTypeRaw}");
|
LoggerInbound($"Received message from {msg.Sender}, Type: {(MessageType)msgTypeRaw}");
|
||||||
|
|
||||||
switch ((MessageType)msgTypeRaw)
|
switch ((MessageType)msgTypeRaw)
|
||||||
{
|
{
|
||||||
case MessageType.PlaceSticker:
|
case MessageType.PlaceSticker:
|
||||||
HandlePlaceSticker(msg);
|
HandlePlaceSticker(msg);
|
||||||
break;
|
break;
|
||||||
case MessageType.ClearSticker:
|
case MessageType.ClearSticker:
|
||||||
HandleClearSticker(msg);
|
HandleClearSticker(msg);
|
||||||
break;
|
break;
|
||||||
case MessageType.ClearAllStickers:
|
case MessageType.ClearAllStickers:
|
||||||
HandleClearAllStickers(msg);
|
HandleClearAllStickers(msg);
|
||||||
break;
|
break;
|
||||||
case MessageType.StartTexture:
|
case MessageType.StartTexture:
|
||||||
HandleStartTexture(msg);
|
HandleStartTexture(msg);
|
||||||
break;
|
break;
|
||||||
case MessageType.SendTexture:
|
case MessageType.SendTexture:
|
||||||
HandleSendTexture(msg);
|
HandleSendTexture(msg);
|
||||||
break;
|
break;
|
||||||
case MessageType.EndTexture:
|
case MessageType.EndTexture:
|
||||||
HandleEndTexture(msg);
|
HandleEndTexture(msg);
|
||||||
break;
|
break;
|
||||||
case MessageType.RequestTexture:
|
case MessageType.RequestTexture:
|
||||||
HandleRequestTexture(msg);
|
HandleRequestTexture(msg);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LoggerInbound($"Invalid message type received: {msgTypeRaw}");
|
LoggerInbound($"Invalid message type received: {msgTypeRaw}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
LoggerInbound($"Error handling message from {msg.Sender}: {e.Message}", true);
|
LoggerInbound($"Error handling message from {msg.Sender}: {e.Message}", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandlePlaceSticker(ModNetworkMessage msg)
|
private static void HandlePlaceSticker(ModNetworkMessage msg)
|
||||||
{
|
{
|
||||||
msg.Read(out int stickerSlot);
|
msg.Read(out int stickerSlot);
|
||||||
msg.Read(out Guid textureHash);
|
msg.Read(out Guid textureHash);
|
||||||
msg.Read(out Vector3 position);
|
msg.Read(out Vector3 position);
|
||||||
msg.Read(out Vector3 forward);
|
msg.Read(out Vector3 forward);
|
||||||
msg.Read(out Vector3 up);
|
msg.Read(out Vector3 up);
|
||||||
msg.Read(out int size);
|
msg.Read(out int size);
|
||||||
msg.Read(out float opacity);
|
msg.Read(out float opacity);
|
||||||
|
|
||||||
if (!StickerSystem.Instance.HasTextureHash(msg.Sender, textureHash))
|
if (!StickerSystem.Instance.HasTextureHash(msg.Sender, textureHash))
|
||||||
{
|
{
|
||||||
SendRequestTexture(stickerSlot, textureHash);
|
SendRequestTexture(stickerSlot, textureHash);
|
||||||
StickerSystem.Instance.ClearStickersForPlayer(msg.Sender, stickerSlot); // Ensure no exploit
|
StickerSystem.Instance.ClearStickersForPlayer(msg.Sender, stickerSlot); // Ensure no exploit
|
||||||
}
|
}
|
||||||
|
|
||||||
StickerSystem.Instance.OnStickerPlaceReceived(msg.Sender, stickerSlot, position, forward, up, (StickerSize)size, opacity);
|
StickerSystem.Instance.OnStickerPlaceReceived(msg.Sender, stickerSlot, position, forward, up, (StickerSize)size, opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandleClearSticker(ModNetworkMessage msg)
|
private static void HandleClearSticker(ModNetworkMessage msg)
|
||||||
{
|
{
|
||||||
msg.Read(out int stickerSlot);
|
msg.Read(out int stickerSlot);
|
||||||
StickerSystem.Instance.OnStickerClearReceived(msg.Sender, stickerSlot);
|
StickerSystem.Instance.OnStickerClearReceived(msg.Sender, stickerSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandleClearAllStickers(ModNetworkMessage msg)
|
private static void HandleClearAllStickers(ModNetworkMessage msg)
|
||||||
{
|
{
|
||||||
StickerSystem.Instance.OnStickerClearAllReceived(msg.Sender);
|
StickerSystem.Instance.OnStickerClearAllReceived(msg.Sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandleStartTexture(ModNetworkMessage msg)
|
private static void HandleStartTexture(ModNetworkMessage msg)
|
||||||
{
|
{
|
||||||
string sender = msg.Sender;
|
string sender = msg.Sender;
|
||||||
msg.Read(out int stickerSlot);
|
msg.Read(out int stickerSlot);
|
||||||
msg.Read(out Guid textureHash);
|
msg.Read(out Guid textureHash);
|
||||||
msg.Read(out int chunkCount);
|
msg.Read(out int chunkCount);
|
||||||
msg.Read(out int width);
|
msg.Read(out int width);
|
||||||
msg.Read(out int height);
|
msg.Read(out int height);
|
||||||
|
|
||||||
if (_textureChunkBuffers.ContainsKey(sender))
|
if (_textureChunkBuffers.ContainsKey(sender))
|
||||||
{
|
{
|
||||||
LoggerInbound($"Received StartTexture message from {sender} while still receiving texture data!");
|
LoggerInbound($"Received StartTexture message from {sender} while still receiving texture data!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StickerSystem.Instance.HasTextureHash(sender, textureHash))
|
if (StickerSystem.Instance.HasTextureHash(sender, textureHash))
|
||||||
{
|
{
|
||||||
LoggerInbound($"Received StartTexture message from {sender} with existing texture hash {textureHash}, skipping texture data.");
|
LoggerInbound($"Received StartTexture message from {sender} with existing texture hash {textureHash}, skipping texture data.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chunkCount > MaxChunkCount)
|
if (chunkCount > MaxChunkCount)
|
||||||
{
|
{
|
||||||
LoggerInbound($"Received StartTexture message from {sender} with too many chunks: {chunkCount}", true);
|
LoggerInbound($"Received StartTexture message from {sender} with too many chunks: {chunkCount}", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_textureMetadata[sender] = (stickerSlot, textureHash, width, height);
|
_textureMetadata[sender] = (stickerSlot, textureHash, width, height);
|
||||||
_textureChunkBuffers[sender] = new byte[Mathf.Clamp(chunkCount * ChunkSize, 0, MaxTextureSize)];
|
_textureChunkBuffers[sender] = new byte[Mathf.Clamp(chunkCount * ChunkSize, 0, MaxTextureSize)];
|
||||||
_expectedChunkCounts[sender] = Mathf.Clamp(chunkCount, 0, MaxChunkCount);
|
_expectedChunkCounts[sender] = Mathf.Clamp(chunkCount, 0, MaxChunkCount);
|
||||||
_receivedChunkCounts[sender] = 0;
|
_receivedChunkCounts[sender] = 0;
|
||||||
|
|
||||||
LoggerInbound($"Received StartTexture message from {sender}: Slot: {stickerSlot}, Hash: {textureHash}, Chunks: {chunkCount}, Resolution: {width}x{height}");
|
LoggerInbound($"Received StartTexture message from {sender}: Slot: {stickerSlot}, Hash: {textureHash}, Chunks: {chunkCount}, Resolution: {width}x{height}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandleSendTexture(ModNetworkMessage msg)
|
private static void HandleSendTexture(ModNetworkMessage msg)
|
||||||
{
|
{
|
||||||
string sender = msg.Sender;
|
string sender = msg.Sender;
|
||||||
msg.Read(out int chunkIdx);
|
msg.Read(out int chunkIdx);
|
||||||
msg.Read(out byte[] chunkData);
|
msg.Read(out byte[] chunkData);
|
||||||
|
|
||||||
if (!_textureChunkBuffers.TryGetValue(sender, out var buffer))
|
if (!_textureChunkBuffers.TryGetValue(sender, out var buffer))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int startIndex = chunkIdx * ChunkSize;
|
int startIndex = chunkIdx * ChunkSize;
|
||||||
Array.Copy(chunkData, 0, buffer, startIndex, chunkData.Length);
|
Array.Copy(chunkData, 0, buffer, startIndex, chunkData.Length);
|
||||||
|
|
||||||
_receivedChunkCounts[sender]++;
|
_receivedChunkCounts[sender]++;
|
||||||
if (_receivedChunkCounts[sender] < _expectedChunkCounts[sender])
|
if (_receivedChunkCounts[sender] < _expectedChunkCounts[sender])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
(int stickerSlot, Guid Hash, int Width, int Height) metadata = _textureMetadata[sender];
|
(int stickerSlot, Guid Hash, int Width, int Height) metadata = _textureMetadata[sender];
|
||||||
|
|
||||||
// All chunks received, reassemble texture
|
// All chunks received, reassemble texture
|
||||||
_textureChunkBuffers.Remove(sender);
|
_textureChunkBuffers.Remove(sender);
|
||||||
_receivedChunkCounts.Remove(sender);
|
_receivedChunkCounts.Remove(sender);
|
||||||
_expectedChunkCounts.Remove(sender);
|
_expectedChunkCounts.Remove(sender);
|
||||||
_textureMetadata.Remove(sender);
|
_textureMetadata.Remove(sender);
|
||||||
|
|
||||||
// Validate image
|
// Validate image
|
||||||
if (!ImageUtility.IsValidImage(buffer))
|
if (!ImageUtility.IsValidImage(buffer))
|
||||||
{
|
{
|
||||||
LoggerInbound($"[Inbound] Received texture data is not a valid image from {sender}!", true);
|
LoggerInbound($"[Inbound] Received texture data is not a valid image from {sender}!", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate data TODO: fix hash???????
|
// Validate data TODO: fix hash???????
|
||||||
(Guid imageHash, int width, int height) = ImageUtility.ExtractImageInfo(buffer);
|
(Guid imageHash, int width, int height) = ImageUtility.ExtractImageInfo(buffer);
|
||||||
if (metadata.Width != width
|
if (metadata.Width != width
|
||||||
|| metadata.Height != height)
|
|| metadata.Height != height)
|
||||||
{
|
{
|
||||||
LoggerInbound($"Received texture data does not match metadata! Expected: {metadata.Hash} ({metadata.Width}x{metadata.Height}), received: {imageHash} ({width}x{height})", true);
|
LoggerInbound($"Received texture data does not match metadata! Expected: {metadata.Hash} ({metadata.Width}x{metadata.Height}), received: {imageHash} ({width}x{height})", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture2D texture = new(1,1);
|
Texture2D texture = new(1,1);
|
||||||
texture.LoadImage(buffer);
|
texture.LoadImage(buffer);
|
||||||
texture.Compress(true);
|
texture.Compress(true);
|
||||||
|
|
||||||
StickerSystem.Instance.OnPlayerStickerTextureReceived(sender, metadata.Hash, texture, metadata.stickerSlot);
|
StickerSystem.Instance.OnPlayerStickerTextureReceived(sender, metadata.Hash, texture, metadata.stickerSlot);
|
||||||
|
|
||||||
LoggerInbound($"All chunks received and texture reassembled from {sender}. " +
|
LoggerInbound($"All chunks received and texture reassembled from {sender}. " +
|
||||||
$"Texture size: {metadata.Width}x{metadata.Height}");
|
$"Texture size: {metadata.Width}x{metadata.Height}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandleEndTexture(ModNetworkMessage msg)
|
private static void HandleEndTexture(ModNetworkMessage msg)
|
||||||
{
|
{
|
||||||
string sender = msg.Sender;
|
string sender = msg.Sender;
|
||||||
if (!_textureChunkBuffers.ContainsKey(sender))
|
if (!_textureChunkBuffers.ContainsKey(sender))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LoggerInbound($"Received EndTexture message without all chunks received from {sender}! Only {_receivedChunkCounts[sender]} out of {_expectedChunkCounts[sender]} received.");
|
LoggerInbound($"Received EndTexture message without all chunks received from {sender}! Only {_receivedChunkCounts[sender]} out of {_expectedChunkCounts[sender]} received.");
|
||||||
|
|
||||||
_textureChunkBuffers.Remove(sender);
|
_textureChunkBuffers.Remove(sender);
|
||||||
_receivedChunkCounts.Remove(sender);
|
_receivedChunkCounts.Remove(sender);
|
||||||
_expectedChunkCounts.Remove(sender);
|
_expectedChunkCounts.Remove(sender);
|
||||||
_textureMetadata.Remove(sender);
|
_textureMetadata.Remove(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandleRequestTexture(ModNetworkMessage msg)
|
private static void HandleRequestTexture(ModNetworkMessage msg)
|
||||||
{
|
{
|
||||||
string sender = msg.Sender;
|
string sender = msg.Sender;
|
||||||
msg.Read(out int stickerSlot);
|
msg.Read(out int stickerSlot);
|
||||||
msg.Read(out Guid textureHash);
|
msg.Read(out Guid textureHash);
|
||||||
|
|
||||||
if (!_isSubscribedToModNetwork || IsSendingTexture)
|
if (!_isSubscribedToModNetwork || IsSendingTexture)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (stickerSlot < 0 || stickerSlot >= _textureStorage.Length)
|
if (stickerSlot < 0 || stickerSlot >= _textureStorage.Length)
|
||||||
{
|
{
|
||||||
LoggerInbound($"Received RequestTexture message from {sender} with invalid slot {stickerSlot}!");
|
LoggerInbound($"Received RequestTexture message from {sender} with invalid slot {stickerSlot}!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_textureStorage[stickerSlot].textureHash != textureHash)
|
if (_textureStorage[stickerSlot].textureHash != textureHash)
|
||||||
{
|
{
|
||||||
LoggerInbound($"Received RequestTexture message from {sender} with invalid texture hash {textureHash} for slot {stickerSlot}!");
|
LoggerInbound($"Received RequestTexture message from {sender} with invalid texture hash {textureHash} for slot {stickerSlot}!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTexture(stickerSlot);
|
SendTexture(stickerSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Inbound Methods
|
#endregion Inbound Methods
|
||||||
}
|
}
|
||||||
|
|
@ -1,140 +1,140 @@
|
||||||
using ABI_RC.Core.IO;
|
using ABI_RC.Core.IO;
|
||||||
using ABI_RC.Core.Networking.IO.Instancing;
|
using ABI_RC.Core.Networking.IO.Instancing;
|
||||||
using ABI_RC.Core.UI;
|
using ABI_RC.Core.UI;
|
||||||
using ABI_RC.Systems.GameEventSystem;
|
using ABI_RC.Systems.GameEventSystem;
|
||||||
using NAK.Stickers.Networking;
|
using NAK.Stickers.Networking;
|
||||||
using NAK.Stickers.Utilities;
|
using NAK.Stickers.Utilities;
|
||||||
using ABI.CCK.Components;
|
using ABI.CCK.Components;
|
||||||
using NAK.Stickers.Integrations;
|
using NAK.Stickers.Integrations;
|
||||||
|
|
||||||
namespace NAK.Stickers;
|
namespace NAK.Stickers;
|
||||||
|
|
||||||
public partial class StickerSystem
|
public partial class StickerSystem
|
||||||
{
|
{
|
||||||
#region Singleton
|
#region Singleton
|
||||||
|
|
||||||
public static StickerSystem Instance { get; private set; }
|
public static StickerSystem Instance { get; private set; }
|
||||||
|
|
||||||
public static void Initialize()
|
public static void Initialize()
|
||||||
{
|
{
|
||||||
if (Instance != null)
|
if (Instance != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Instance = new StickerSystem();
|
Instance = new StickerSystem();
|
||||||
|
|
||||||
// configure decalery
|
// configure decalery
|
||||||
DecalManager.SetPreferredMode(DecalUtils.Mode.GPU, false, 0);
|
DecalManager.SetPreferredMode(DecalUtils.Mode.GPU, false, 0);
|
||||||
|
|
||||||
// ensure cache folder exists
|
// ensure cache folder exists
|
||||||
EnsureStickersFolderExists();
|
EnsureStickersFolderExists();
|
||||||
|
|
||||||
// listen for game events
|
// listen for game events
|
||||||
CVRGameEventSystem.Initialization.OnPlayerSetupStart.AddListener(Instance.OnPlayerSetupStart);
|
CVRGameEventSystem.Initialization.OnPlayerSetupStart.AddListener(Instance.OnPlayerSetupStart);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Singleton
|
#endregion Singleton
|
||||||
|
|
||||||
#region Callback Registration
|
#region Callback Registration
|
||||||
|
|
||||||
private void OnPlayerSetupStart()
|
private void OnPlayerSetupStart()
|
||||||
{
|
{
|
||||||
// TODO: this can be spammed by world author toggling CVRWorld.enabled state
|
// TODO: this can be spammed by world author toggling CVRWorld.enabled state
|
||||||
CVRGameEventSystem.World.OnLoad.AddListener(_ => OnWorldLoad());
|
CVRGameEventSystem.World.OnLoad.AddListener(_ => OnWorldLoad());
|
||||||
CVRGameEventSystem.World.OnUnload.AddListener(_ => OnWorldUnload());
|
CVRGameEventSystem.World.OnUnload.AddListener(_ => OnWorldUnload());
|
||||||
CVRGameEventSystem.Instance.OnConnected.AddListener((_) => { if (!Instances.IsReconnecting) OnInitialConnection(); });
|
CVRGameEventSystem.Instance.OnConnected.AddListener((_) => { if (!Instances.IsReconnecting) OnInitialConnection(); });
|
||||||
|
|
||||||
CVRGameEventSystem.Player.OnJoinEntity.AddListener(Instance.OnPlayerJoined);
|
CVRGameEventSystem.Player.OnJoinEntity.AddListener(Instance.OnPlayerJoined);
|
||||||
CVRGameEventSystem.Player.OnLeaveEntity.AddListener(Instance.OnPlayerLeft);
|
CVRGameEventSystem.Player.OnLeaveEntity.AddListener(Instance.OnPlayerLeft);
|
||||||
BetterScheduleSystem.AddJob(Instance.OnUpdate, 10f, -1);
|
BetterScheduleSystem.AddJob(Instance.OnUpdate, 10f, -1);
|
||||||
LoadAllImagesAtStartup();
|
LoadAllImagesAtStartup();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Callback Registration
|
#endregion Callback Registration
|
||||||
|
|
||||||
#region Game Events
|
#region Game Events
|
||||||
|
|
||||||
private void OnInitialConnection()
|
private void OnInitialConnection()
|
||||||
{
|
{
|
||||||
ClearStickersSelf(); // clear stickers on remotes just in case we rejoined
|
ClearStickersSelf(); // clear stickers on remotes just in case we rejoined
|
||||||
ModNetwork.Reset(); // reset network buffers and metadata
|
ModNetwork.Reset(); // reset network buffers and metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnWorldLoad()
|
private void OnWorldLoad()
|
||||||
{
|
{
|
||||||
CVRDataStore worldDS = CVRWorld.Instance.DataStore;
|
CVRDataStore worldDS = CVRWorld.Instance.DataStore;
|
||||||
// IsRestrictedInstance = worldDS && worldDS.GetValue<bool>("StickersMod-ForceDisable");
|
// IsRestrictedInstance = worldDS && worldDS.GetValue<bool>("StickersMod-ForceDisable");
|
||||||
if (IsRestrictedInstance) StickerMod.Logger.Msg("Stickers are restricted by the world author.");
|
if (IsRestrictedInstance) StickerMod.Logger.Msg("Stickers are restricted by the world author.");
|
||||||
BTKUIAddon.OnStickerRestrictionUpdated(IsRestrictedInstance);
|
BTKUIAddon.OnStickerRestrictionUpdated(IsRestrictedInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnWorldUnload()
|
private void OnWorldUnload()
|
||||||
{
|
{
|
||||||
IsRestrictedInstance = false;
|
IsRestrictedInstance = false;
|
||||||
CleanupAllButSelf();
|
CleanupAllButSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Game Events
|
#endregion Game Events
|
||||||
|
|
||||||
#region Data
|
#region Data
|
||||||
|
|
||||||
// private bool _isEnabled = true;
|
// private bool _isEnabled = true;
|
||||||
//
|
//
|
||||||
// public bool IsEnabled
|
// public bool IsEnabled
|
||||||
// {
|
// {
|
||||||
// get => _isEnabled;
|
// get => _isEnabled;
|
||||||
// set
|
// set
|
||||||
// {
|
// {
|
||||||
// if (_isEnabled == value)
|
// if (_isEnabled == value)
|
||||||
// return;
|
// return;
|
||||||
//
|
//
|
||||||
// _isEnabled = value;
|
// _isEnabled = value;
|
||||||
// if (!_isEnabled) ClearAllStickers();
|
// if (!_isEnabled) ClearAllStickers();
|
||||||
// ModNetwork.IsEnabled = _isEnabled;
|
// ModNetwork.IsEnabled = _isEnabled;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public bool IsRestrictedInstance { get; internal set; }
|
public bool IsRestrictedInstance { get; internal set; }
|
||||||
|
|
||||||
private string SelectedStickerName => ModSettings.Hidden_SelectedStickerNames.Value[_selectedStickerSlot];
|
private string SelectedStickerName => ModSettings.Hidden_SelectedStickerNames.Value[_selectedStickerSlot];
|
||||||
|
|
||||||
private const float StickerKillTime = 30f;
|
private const float StickerKillTime = 30f;
|
||||||
private const float StickerCooldown = 0.2f;
|
private const float StickerCooldown = 0.2f;
|
||||||
private readonly Dictionary<string, StickerData> _playerStickers = new();
|
private readonly Dictionary<string, StickerData> _playerStickers = new();
|
||||||
internal const string PlayerLocalId = "_PLAYERLOCAL";
|
internal const string PlayerLocalId = "_PLAYERLOCAL";
|
||||||
|
|
||||||
private int _selectedStickerSlot;
|
private int _selectedStickerSlot;
|
||||||
public int SelectedStickerSlot
|
public int SelectedStickerSlot
|
||||||
{
|
{
|
||||||
get => _selectedStickerSlot;
|
get => _selectedStickerSlot;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_selectedStickerSlot = value < 0 ? ModSettings.MaxStickerSlots - 1 : value % ModSettings.MaxStickerSlots;
|
_selectedStickerSlot = value < 0 ? ModSettings.MaxStickerSlots - 1 : value % ModSettings.MaxStickerSlots;
|
||||||
IsInStickerMode = IsInStickerMode; // refresh sticker mode
|
IsInStickerMode = IsInStickerMode; // refresh sticker mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _isInStickerMode;
|
private bool _isInStickerMode;
|
||||||
public bool IsInStickerMode
|
public bool IsInStickerMode
|
||||||
{
|
{
|
||||||
get => _isInStickerMode;
|
get => _isInStickerMode;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_isInStickerMode = value && !IsRestrictedInstance; // ensure cannot enter when restricted
|
_isInStickerMode = value && !IsRestrictedInstance; // ensure cannot enter when restricted
|
||||||
if (_isInStickerMode)
|
if (_isInStickerMode)
|
||||||
{
|
{
|
||||||
CohtmlHud.Instance.SelectPropToSpawn(
|
CohtmlHud.Instance.SelectPropToSpawn(
|
||||||
StickerCache.GetCohtmlResourcesPath(SelectedStickerName),
|
StickerCache.GetCohtmlResourcesPath(SelectedStickerName),
|
||||||
Path.GetFileNameWithoutExtension(SelectedStickerName),
|
Path.GetFileNameWithoutExtension(SelectedStickerName),
|
||||||
"Sticker selected for stickering:");
|
"Sticker selected for stickering:");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CohtmlHud.Instance.ClearPropToSpawn();
|
CohtmlHud.Instance.ClearPropToSpawn();
|
||||||
ClearStickerPreview();
|
ClearStickerPreview();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Data
|
#endregion Data
|
||||||
}
|
}
|
||||||
|
|
@ -1,195 +1,195 @@
|
||||||
using ABI_RC.Core;
|
using ABI_RC.Core;
|
||||||
using ABI_RC.Core.Player;
|
using ABI_RC.Core.Player;
|
||||||
using ABI_RC.Systems.InputManagement;
|
using ABI_RC.Systems.InputManagement;
|
||||||
using NAK.Stickers.Networking;
|
using NAK.Stickers.Networking;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace NAK.Stickers;
|
namespace NAK.Stickers;
|
||||||
|
|
||||||
public partial class StickerSystem
|
public partial class StickerSystem
|
||||||
{
|
{
|
||||||
#region Sticker Lifecycle
|
#region Sticker Lifecycle
|
||||||
|
|
||||||
private StickerData GetOrCreateStickerData(string playerId)
|
private StickerData GetOrCreateStickerData(string playerId)
|
||||||
{
|
{
|
||||||
if (_playerStickers.TryGetValue(playerId, out StickerData stickerData))
|
if (_playerStickers.TryGetValue(playerId, out StickerData stickerData))
|
||||||
return stickerData;
|
return stickerData;
|
||||||
|
|
||||||
stickerData = new StickerData(playerId, ModSettings.MaxStickerSlots);
|
stickerData = new StickerData(playerId, ModSettings.MaxStickerSlots);
|
||||||
_playerStickers[playerId] = stickerData;
|
_playerStickers[playerId] = stickerData;
|
||||||
return stickerData;
|
return stickerData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlaceStickerFromControllerRay(Transform transform, CVRHand hand = CVRHand.Left, bool isPreview = false)
|
public void PlaceStickerFromControllerRay(Transform transform, CVRHand hand = CVRHand.Left, bool isPreview = false)
|
||||||
{
|
{
|
||||||
Vector3 controllerForward = transform.forward;
|
Vector3 controllerForward = transform.forward;
|
||||||
Vector3 controllerUp = transform.up;
|
Vector3 controllerUp = transform.up;
|
||||||
Vector3 playerUp = PlayerSetup.Instance.transform.up;
|
Vector3 playerUp = PlayerSetup.Instance.transform.up;
|
||||||
|
|
||||||
// extracting angle of controller ray on forward axis
|
// extracting angle of controller ray on forward axis
|
||||||
Vector3 projectedControllerUp = Vector3.ProjectOnPlane(controllerUp, controllerForward).normalized;
|
Vector3 projectedControllerUp = Vector3.ProjectOnPlane(controllerUp, controllerForward).normalized;
|
||||||
Vector3 projectedPlayerUp = Vector3.ProjectOnPlane(playerUp, controllerForward).normalized;
|
Vector3 projectedPlayerUp = Vector3.ProjectOnPlane(playerUp, controllerForward).normalized;
|
||||||
float angle = Vector3.Angle(projectedControllerUp, projectedPlayerUp);
|
float angle = Vector3.Angle(projectedControllerUp, projectedPlayerUp);
|
||||||
|
|
||||||
float angleThreshold = ModSettings.Entry_PlayerUpAlignmentThreshold.Value;
|
float angleThreshold = ModSettings.Entry_PlayerUpAlignmentThreshold.Value;
|
||||||
Vector3 targetUp = (angleThreshold != 0f && angle <= angleThreshold)
|
Vector3 targetUp = (angleThreshold != 0f && angle <= angleThreshold)
|
||||||
// leave 0.01% of the controller up vector to prevent issues with alignment on floor & ceiling in Desktop
|
// leave 0.01% of the controller up vector to prevent issues with alignment on floor & ceiling in Desktop
|
||||||
? Vector3.Slerp(controllerUp, playerUp, 0.99f)
|
? Vector3.Slerp(controllerUp, playerUp, 0.99f)
|
||||||
: controllerUp;
|
: controllerUp;
|
||||||
|
|
||||||
if (isPreview)
|
if (isPreview)
|
||||||
{
|
{
|
||||||
PlaceStickerPreview(transform.position, controllerForward, targetUp);
|
PlaceStickerPreview(transform.position, controllerForward, targetUp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PlaceStickerSelf(transform.position, transform.forward, targetUp))
|
if (!PlaceStickerSelf(transform.position, transform.forward, targetUp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CVRInputManager.Instance.Vibrate(0f, 0.1f, 10f, 0.1f, hand);
|
CVRInputManager.Instance.Vibrate(0f, 0.1f, 10f, 0.1f, hand);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool PlaceStickerSelf(Vector3 position, Vector3 forward, Vector3 up, bool alignWithNormal = true)
|
private bool PlaceStickerSelf(Vector3 position, Vector3 forward, Vector3 up, bool alignWithNormal = true)
|
||||||
{
|
{
|
||||||
if (!AttemptPlaceSticker(PlayerLocalId, ModSettings.Entry_StickerSize.Value, ModSettings.Entry_StickerOpacity.Value, position, forward, up, alignWithNormal, SelectedStickerSlot))
|
if (!AttemptPlaceSticker(PlayerLocalId, ModSettings.Entry_StickerSize.Value, ModSettings.Entry_StickerOpacity.Value, position, forward, up, alignWithNormal, SelectedStickerSlot))
|
||||||
return false; // failed
|
return false; // failed
|
||||||
|
|
||||||
// placed, now network
|
// placed, now network
|
||||||
ModNetwork.SendPlaceSticker(SelectedStickerSlot, position, forward, up, ModSettings.Entry_StickerSize.Value, ModSettings.Entry_StickerOpacity.Value);
|
ModNetwork.SendPlaceSticker(SelectedStickerSlot, position, forward, up, ModSettings.Entry_StickerSize.Value, ModSettings.Entry_StickerOpacity.Value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AttemptPlaceSticker(string playerId, StickerSize size, float opacity, Vector3 position, Vector3 forward, Vector3 up, bool alignWithNormal = true, int stickerSlot = 0, bool isPreview = false)
|
private bool AttemptPlaceSticker(string playerId, StickerSize size, float opacity, Vector3 position, Vector3 forward, Vector3 up, bool alignWithNormal = true, int stickerSlot = 0, bool isPreview = false)
|
||||||
{
|
{
|
||||||
// if the world contained a gameobject with the [DisableStickers] name and restricted the instance disable stickers!
|
// if the world contained a gameobject with the [DisableStickers] name and restricted the instance disable stickers!
|
||||||
if (IsRestrictedInstance)
|
if (IsRestrictedInstance)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
StickerData stickerData = GetOrCreateStickerData(playerId);
|
StickerData stickerData = GetOrCreateStickerData(playerId);
|
||||||
if (Time.time - stickerData.LastPlacedTime < StickerCooldown)
|
if (Time.time - stickerData.LastPlacedTime < StickerCooldown)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Every layer other than IgnoreRaycast, PlayerLocal, PlayerClone, PlayerNetwork, and UI Internal
|
// Every layer other than IgnoreRaycast, PlayerLocal, PlayerClone, PlayerNetwork, and UI Internal
|
||||||
const int LayerMask = ~((1 << 2) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 15));
|
const int LayerMask = ~((1 << 2) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 15));
|
||||||
if (!Physics.Raycast(position, forward, out RaycastHit hit,
|
if (!Physics.Raycast(position, forward, out RaycastHit hit,
|
||||||
10f, LayerMask, QueryTriggerInteraction.Ignore))
|
10f, LayerMask, QueryTriggerInteraction.Ignore))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// if gameobject name starts with [NoSticker] then don't place sticker
|
// if gameobject name starts with [NoSticker] then don't place sticker
|
||||||
if (hit.transform.gameObject.name.StartsWith("[NoSticker]"))
|
if (hit.transform.gameObject.name.StartsWith("[NoSticker]"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (isPreview)
|
if (isPreview)
|
||||||
{
|
{
|
||||||
stickerData.PlacePreview(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot, size);
|
stickerData.PlacePreview(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot, size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
stickerData.Place(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot, size, opacity);
|
stickerData.Place(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot, size, opacity);
|
||||||
stickerData.PlayAudio();
|
stickerData.PlayAudio();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearStickersSelf()
|
public void ClearStickersSelf()
|
||||||
{
|
{
|
||||||
ClearStickersForPlayer(PlayerLocalId);
|
ClearStickersForPlayer(PlayerLocalId);
|
||||||
ModNetwork.SendClearAllStickers();
|
ModNetwork.SendClearAllStickers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearStickerSelf(int stickerSlot)
|
public void ClearStickerSelf(int stickerSlot)
|
||||||
{
|
{
|
||||||
ClearStickersForPlayer(PlayerLocalId, stickerSlot);
|
ClearStickersForPlayer(PlayerLocalId, stickerSlot);
|
||||||
ModNetwork.SendClearSticker(stickerSlot);
|
ModNetwork.SendClearSticker(stickerSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearStickersForPlayer(string playerId)
|
private void ClearStickersForPlayer(string playerId)
|
||||||
{
|
{
|
||||||
if (!_playerStickers.TryGetValue(playerId, out StickerData stickerData))
|
if (!_playerStickers.TryGetValue(playerId, out StickerData stickerData))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
stickerData.Clear();
|
stickerData.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearStickersForPlayer(string playerId, int stickerSlot)
|
public void ClearStickersForPlayer(string playerId, int stickerSlot)
|
||||||
{
|
{
|
||||||
if (!_playerStickers.TryGetValue(playerId, out StickerData stickerData))
|
if (!_playerStickers.TryGetValue(playerId, out StickerData stickerData))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
stickerData.Clear(stickerSlot);
|
stickerData.Clear(stickerSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetTextureSelf(byte[] imageBytes, int stickerSlot = 0)
|
private void SetTextureSelf(byte[] imageBytes, int stickerSlot = 0)
|
||||||
{
|
{
|
||||||
Texture2D texture = new(1, 1); // placeholder
|
Texture2D texture = new(1, 1); // placeholder
|
||||||
texture.LoadImage(imageBytes);
|
texture.LoadImage(imageBytes);
|
||||||
texture.Compress(true); // noachi said to do
|
texture.Compress(true); // noachi said to do
|
||||||
|
|
||||||
ClearStickerSelf(stickerSlot); // clear placed stickers in-scene so we can't replace an entire wall at once
|
ClearStickerSelf(stickerSlot); // clear placed stickers in-scene so we can't replace an entire wall at once
|
||||||
OnPlayerStickerTextureReceived(PlayerLocalId, Guid.Empty, texture, stickerSlot);
|
OnPlayerStickerTextureReceived(PlayerLocalId, Guid.Empty, texture, stickerSlot);
|
||||||
ModNetwork.SetTexture(stickerSlot, imageBytes);
|
ModNetwork.SetTexture(stickerSlot, imageBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearAllStickers()
|
public void ClearAllStickers()
|
||||||
{
|
{
|
||||||
foreach (StickerData stickerData in _playerStickers.Values)
|
foreach (StickerData stickerData in _playerStickers.Values)
|
||||||
stickerData.Clear();
|
stickerData.Clear();
|
||||||
|
|
||||||
ModNetwork.SendClearAllStickers();
|
ModNetwork.SendClearAllStickers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnPlayerStickerTextureReceived(string playerId, Guid textureHash, Texture2D texture, int stickerSlot = 0)
|
public void OnPlayerStickerTextureReceived(string playerId, Guid textureHash, Texture2D texture, int stickerSlot = 0)
|
||||||
{
|
{
|
||||||
StickerData stickerData = GetOrCreateStickerData(playerId);
|
StickerData stickerData = GetOrCreateStickerData(playerId);
|
||||||
stickerData.SetTexture(textureHash, texture, stickerSlot);
|
stickerData.SetTexture(textureHash, texture, stickerSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasTextureHash(string playerId, Guid textureHash)
|
public bool HasTextureHash(string playerId, Guid textureHash)
|
||||||
{
|
{
|
||||||
StickerData stickerData = GetOrCreateStickerData(playerId);
|
StickerData stickerData = GetOrCreateStickerData(playerId);
|
||||||
return stickerData.CheckHasTextureHash(textureHash);
|
return stickerData.CheckHasTextureHash(textureHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CleanupAll()
|
public void CleanupAll()
|
||||||
{
|
{
|
||||||
foreach ((_, StickerData data) in _playerStickers)
|
foreach ((_, StickerData data) in _playerStickers)
|
||||||
data.Cleanup();
|
data.Cleanup();
|
||||||
|
|
||||||
_playerStickers.Clear();
|
_playerStickers.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CleanupAllButSelf()
|
public void CleanupAllButSelf()
|
||||||
{
|
{
|
||||||
StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId);
|
StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId);
|
||||||
|
|
||||||
foreach ((_, StickerData data) in _playerStickers)
|
foreach ((_, StickerData data) in _playerStickers)
|
||||||
{
|
{
|
||||||
if (data == localStickerData) data.Clear();
|
if (data == localStickerData) data.Clear();
|
||||||
else data.Cleanup();
|
else data.Cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
_playerStickers.Clear();
|
_playerStickers.Clear();
|
||||||
_playerStickers[PlayerLocalId] = localStickerData;
|
_playerStickers[PlayerLocalId] = localStickerData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlaceStickerPreview(Vector3 position, Vector3 forward, Vector3 up)
|
public void PlaceStickerPreview(Vector3 position, Vector3 forward, Vector3 up)
|
||||||
{
|
{
|
||||||
AttemptPlaceSticker(PlayerLocalId, ModSettings.Entry_StickerSize.Value, ModSettings.Entry_StickerOpacity.Value, position, forward, up, true, SelectedStickerSlot, true);
|
AttemptPlaceSticker(PlayerLocalId, ModSettings.Entry_StickerSize.Value, ModSettings.Entry_StickerOpacity.Value, position, forward, up, true, SelectedStickerSlot, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateStickerPreview()
|
public void UpdateStickerPreview()
|
||||||
{
|
{
|
||||||
if (!IsInStickerMode) return;
|
if (!IsInStickerMode) return;
|
||||||
|
|
||||||
StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId);
|
StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId);
|
||||||
localStickerData.ClearPreview(); // clear prior frames sticker preview
|
localStickerData.ClearPreview(); // clear prior frames sticker preview
|
||||||
localStickerData.UpdatePreview(SelectedStickerSlot);
|
localStickerData.UpdatePreview(SelectedStickerSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearStickerPreview()
|
public void ClearStickerPreview()
|
||||||
{
|
{
|
||||||
StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId);
|
StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId);
|
||||||
localStickerData.ClearPreview();
|
localStickerData.ClearPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Sticker Lifecycle
|
#endregion Sticker Lifecycle
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue