diff --git a/ChestPvPProtection/ChestPvPProtection.csproj b/ChestPvPProtection/ChestPvPProtection.csproj
new file mode 100644
index 0000000..312cea9
--- /dev/null
+++ b/ChestPvPProtection/ChestPvPProtection.csproj
@@ -0,0 +1,471 @@
+
+
+ netstandard2.1
+ VMods.ChestPvPProtection
+ VMods.ChestPvPProtection
+ A mod that prevents looting of enemy player chests/workstations by players with the PvP Protection buff
+ 1.0.0
+ true
+ latest
+ False
+
+
+
+ M:\Games\Steam\steamapps\common\VRising\BepInEx\unhollowed
+ M:\Games\Steam\steamapps\common\VRising\BepInEx\WetstonePlugins
+ M:\Games\Steam\steamapps\common\VRising\VRising_Server\BepInEx\WetstonePlugins
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(UnhollowedDllPath)\com.stunlock.console.dll
+
+
+ $(UnhollowedDllPath)\com.stunlock.metrics.dll
+
+
+ $(UnhollowedDllPath)\com.stunlock.network.lidgren.dll
+
+
+ $(UnhollowedDllPath)\com.stunlock.network.steam.dll
+
+
+ $(UnhollowedDllPath)\Il2CppMono.Security.dll
+
+
+ $(UnhollowedDllPath)\Il2CppSystem.dll
+
+
+ $(UnhollowedDllPath)\Il2CppSystem.Configuration.dll
+
+
+ $(UnhollowedDllPath)\Il2CppSystem.Core.dll
+
+
+ $(UnhollowedDllPath)\Il2CppSystem.Data.dll
+
+
+ $(UnhollowedDllPath)\Il2CppSystem.Numerics.dll
+
+
+ $(UnhollowedDllPath)\Il2CppSystem.Runtime.Serialization.dll
+
+
+ $(UnhollowedDllPath)\Il2CppSystem.Xml.dll
+
+
+ $(UnhollowedDllPath)\Il2CppSystem.Xml.Linq.dll
+
+
+ $(UnhollowedDllPath)\Lidgren.Network.dll
+
+
+ $(UnhollowedDllPath)\MagicaCloth.dll
+
+
+ $(UnhollowedDllPath)\Malee.ReorderableList.dll
+
+
+ $(UnhollowedDllPath)\Newtonsoft.Json.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.Behaviours.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.Camera.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.CastleBuilding.Systems.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.Conversion.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.Gameplay.Scripting.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.Gameplay.Systems.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.GeneratedNetCode.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.Misc.Systems.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.Pathfinding.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.Presentation.Systems.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.Roofs.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.ScriptableSystems.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.Shared.dll
+
+
+ $(UnhollowedDllPath)\Il2Cppmscorlib.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.dll
+
+
+ $(UnhollowedDllPath)\com.stunlock.network.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.Shared.Systems.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.Terrain.dll
+
+
+ $(UnhollowedDllPath)\RootMotion.dll
+
+
+ $(UnhollowedDllPath)\Sequencer.dll
+
+
+ $(UnhollowedDllPath)\Stunlock.Fmod.dll
+
+
+ $(UnhollowedDllPath)\Unity.Burst.dll
+
+
+ $(UnhollowedDllPath)\Unity.Burst.Unsafe.dll
+
+
+ $(UnhollowedDllPath)\Unity.Collections.dll
+
+
+ $(UnhollowedDllPath)\Unity.Collections.LowLevel.ILSupport.dll
+
+
+ $(UnhollowedDllPath)\Unity.Deformations.dll
+
+
+ $(UnhollowedDllPath)\Unity.Entities.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.HUD.dll
+
+
+ $(UnhollowedDllPath)\Unity.Entities.Hybrid.dll
+
+
+ $(UnhollowedDllPath)\Unity.Jobs.dll
+
+
+ $(UnhollowedDllPath)\Unity.Mathematics.dll
+
+
+ $(UnhollowedDllPath)\Unity.Mathematics.Extensions.dll
+
+
+ $(UnhollowedDllPath)\Unity.Mathematics.Extensions.Hybrid.dll
+
+
+ $(UnhollowedDllPath)\Unity.Physics.dll
+
+
+ $(UnhollowedDllPath)\Unity.Physics.Hybrid.dll
+
+
+ $(UnhollowedDllPath)\Unity.Properties.dll
+
+
+ $(UnhollowedDllPath)\Unity.Rendering.Hybrid.dll
+
+
+ $(UnhollowedDllPath)\Unity.RenderPipelines.Core.Runtime.dll
+
+
+ $(UnhollowedDllPath)\Unity.RenderPipelines.HighDefinition.Config.Runtime.dll
+
+
+ $(UnhollowedDllPath)\Unity.RenderPipelines.HighDefinition.Runtime.dll
+
+
+ $(UnhollowedDllPath)\Unity.Scenes.dll
+
+
+ $(UnhollowedDllPath)\Unity.Serialization.dll
+
+
+ $(UnhollowedDllPath)\Unity.Services.Analytics.dll
+
+
+ $(UnhollowedDllPath)\Unity.Services.Core.dll
+
+
+ $(UnhollowedDllPath)\Unity.Services.Core.Configuration.dll
+
+
+ $(UnhollowedDllPath)\Unity.Services.Core.Device.dll
+
+
+ $(UnhollowedDllPath)\Unity.Services.Core.Environments.dll
+
+
+ $(UnhollowedDllPath)\Unity.Services.Core.Environments.Internal.dll
+
+
+ $(UnhollowedDllPath)\Unity.Services.Core.Internal.dll
+
+
+ $(UnhollowedDllPath)\Unity.Services.Core.Registration.dll
+
+
+ $(UnhollowedDllPath)\Unity.Services.Core.Scheduler.dll
+
+
+ $(UnhollowedDllPath)\Unity.Services.Core.Telemetry.dll
+
+
+ $(UnhollowedDllPath)\Unity.Services.Core.Threading.dll
+
+
+ $(UnhollowedDllPath)\Unity.TextMeshPro.dll
+
+
+ $(UnhollowedDllPath)\Unity.Transforms.dll
+
+
+ $(UnhollowedDllPath)\Unity.Transforms.Hybrid.dll
+
+
+ $(UnhollowedDllPath)\Unity.VisualEffectGraph.Runtime.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.AccessibilityModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.AIModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.AndroidJNIModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.AnimationModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.ARModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.AssetBundleModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.AudioModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.ClothModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.ClusterInputModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.ClusterRendererModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.CoreModule.dll
+
+
+ $(UnhollowedDllPath)\ProjectM.CodeGeneration.dll
+
+
+ $(UnhollowedDllPath)\Stunlock.Core.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.CrashReportingModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.DirectorModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.DSPGraphModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.GameCenterModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.GIModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.GridModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.HotReloadModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.ImageConversionModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.IMGUIModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.InputLegacyModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.InputModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.JSONSerializeModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.LocalizationModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.ParticleSystemModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.PerformanceReportingModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.Physics2DModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.PhysicsModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.ProfilerModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.ScreenCaptureModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.SharedInternalsModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.SpriteMaskModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.SpriteShapeModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.StreamingModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.SubstanceModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.SubsystemsModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.TerrainModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.TerrainPhysicsModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.TextCoreModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.TextRenderingModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.TilemapModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.TLSModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UI.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UIElementsModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UIElementsNativeModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UIModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UmbraModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UNETModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UnityAnalyticsModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UnityConnectModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UnityCurlModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UnityTestProtocolModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UnityWebRequestAssetBundleModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UnityWebRequestAudioModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UnityWebRequestModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UnityWebRequestTextureModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.UnityWebRequestWWWModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.VehiclesModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.VFXModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.VideoModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.VirtualTexturingModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.VRModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.WindModule.dll
+
+
+ $(UnhollowedDllPath)\UnityEngine.XRModule.dll
+
+
+ $(UnhollowedDllPath)\VivoxUnity.dll
+
+
+
+
+
+
+
+
diff --git a/ChestPvPProtection/Configs/ChestPvPProtectionSystemConfig.cs b/ChestPvPProtection/Configs/ChestPvPProtectionSystemConfig.cs
new file mode 100644
index 0000000..ea4d11d
--- /dev/null
+++ b/ChestPvPProtection/Configs/ChestPvPProtectionSystemConfig.cs
@@ -0,0 +1,25 @@
+using BepInEx.Configuration;
+
+namespace VMods.ChestPvPProtection
+{
+ public static class ChestPvPProtectionSystemConfig
+ {
+ #region Properties
+
+ public static ConfigEntry ChestPvPProtectionEnabled { get; private set; }
+
+ public static ConfigEntry ChestPvPProtectionSendMessage { get; private set; }
+
+ #endregion
+
+ #region Public Methods
+
+ public static void Initialize(ConfigFile config)
+ {
+ ChestPvPProtectionEnabled = config.Bind(nameof(ChestPvPProtection), nameof(ChestPvPProtectionEnabled), false, "Enabled/disable the Chest PvP Protection system.");
+ ChestPvPProtectionSendMessage = config.Bind(nameof(ChestPvPProtection), nameof(ChestPvPProtectionSendMessage), true, "Enabled/disable the sending of a system message to the player with the PvP Protection buff that's attempting to interact with an enemy player's chest.");
+ }
+
+ #endregion
+ }
+}
diff --git a/ChestPvPProtection/Hooks/InventoryHooks.cs b/ChestPvPProtection/Hooks/InventoryHooks.cs
new file mode 100644
index 0000000..997424b
--- /dev/null
+++ b/ChestPvPProtection/Hooks/InventoryHooks.cs
@@ -0,0 +1,158 @@
+using HarmonyLib;
+using ProjectM;
+using ProjectM.Network;
+using Unity.Collections;
+using Unity.Entities;
+using VMods.Shared;
+using Wetstone.API;
+
+namespace VMods.ChestPvPProtection
+{
+ [HarmonyPatch]
+ public static class InventoryHooks
+ {
+ #region Events
+
+ public delegate void InventoryInteractionEventHandler(Entity eventEntity, VModCharacter vmodCharacter, params Entity[] inventoryEntities);
+ public static event InventoryInteractionEventHandler InventoryInteractionEvent;
+ private static void FireInventoryInteractionEvent(Entity eventEntity, VModCharacter vmodCharacter, params Entity[] inventoryEntities) => InventoryInteractionEvent?.Invoke(eventEntity, vmodCharacter, inventoryEntities);
+
+ #endregion
+
+ #region Private Methods
+
+ [HarmonyPatch(typeof(MoveItemBetweenInventoriesSystem), nameof(MoveItemBetweenInventoriesSystem.OnUpdate))]
+ [HarmonyPrefix]
+ private static void MoveItemBetweenInventories(MoveItemBetweenInventoriesSystem __instance)
+ {
+ if(!VWorld.IsServer || __instance._MoveItemBetweenInventoriesEventQuery == null)
+ {
+ return;
+ }
+
+ var server = VWorld.Server;
+ var entityManager = server.EntityManager;
+ var networkIdToEntityMap = __instance._NetworkIdSystem._NetworkIdToEntityMap;
+
+ var entities = __instance._MoveItemBetweenInventoriesEventQuery.ToEntityArray(Allocator.Temp);
+ foreach(var entity in entities)
+ {
+ var fromCharacter = new VModCharacter(entityManager.GetComponentData(entity), entityManager);
+ var moveEvent = entityManager.GetComponentData(entity);
+ FireInventoryInteractionEvent(entity, fromCharacter, moveEvent.FromInventory.GetNetworkedEntity(networkIdToEntityMap)._Entity, moveEvent.ToInventory.GetNetworkedEntity(networkIdToEntityMap)._Entity);
+ }
+ }
+
+ [HarmonyPatch(typeof(MoveAllItemsBetweenInventoriesSystem), nameof(MoveAllItemsBetweenInventoriesSystem.OnUpdate))]
+ [HarmonyPrefix]
+ private static void MoveAllItemsBetweenInventories(MoveAllItemsBetweenInventoriesSystem __instance)
+ {
+ if(!VWorld.IsServer || __instance.__MoveAllItemsJob_entityQuery == null)
+ {
+ return;
+ }
+
+ var server = VWorld.Server;
+ var entityManager = server.EntityManager;
+ var networkIdToEntityMap = __instance._NetworkIdSystem._NetworkIdToEntityMap;
+
+ var entities = __instance.__MoveAllItemsJob_entityQuery.ToEntityArray(Allocator.Temp);
+ foreach(var entity in entities)
+ {
+ var fromCharacter = new VModCharacter(entityManager.GetComponentData(entity), entityManager);
+ var moveEvent = entityManager.GetComponentData(entity);
+ FireInventoryInteractionEvent(entity, fromCharacter, moveEvent.FromInventory.GetNetworkedEntity(networkIdToEntityMap)._Entity, moveEvent.ToInventory.GetNetworkedEntity(networkIdToEntityMap)._Entity);
+ }
+ }
+
+ [HarmonyPatch(typeof(DropInventoryItemSystem), nameof(DropInventoryItemSystem.OnUpdate))]
+ [HarmonyPrefix]
+ private static void DropInventoryItem(DropInventoryItemSystem __instance)
+ {
+ if(!VWorld.IsServer || __instance.__DropInventoryItemJob_entityQuery == null)
+ {
+ return;
+ }
+
+ var server = VWorld.Server;
+ var entityManager = server.EntityManager;
+ var networkIdToEntityMap = __instance._NetworkIdSystem._NetworkIdToEntityMap;
+
+ var entities = __instance.__DropInventoryItemJob_entityQuery.ToEntityArray(Allocator.Temp);
+ foreach(var entity in entities)
+ {
+ var fromCharacter = new VModCharacter(entityManager.GetComponentData(entity), entityManager);
+ var dropEvent = entityManager.GetComponentData(entity);
+ FireInventoryInteractionEvent(entity, fromCharacter, dropEvent.Inventory.GetNetworkedEntity(networkIdToEntityMap)._Entity);
+ }
+ }
+
+ [HarmonyPatch(typeof(SplitItemSystem), nameof(SplitItemSystem.OnUpdate))]
+ [HarmonyPrefix]
+ private static void SplitItem(SplitItemSystem __instance)
+ {
+ if(!VWorld.IsServer || __instance._Query == null)
+ {
+ return;
+ }
+
+ var server = VWorld.Server;
+ var entityManager = server.EntityManager;
+ var networkIdToEntityMap = __instance._NetworkIdSystem._NetworkIdToEntityMap;
+
+ var entities = __instance._Query.ToEntityArray(Allocator.Temp);
+ foreach(var entity in entities)
+ {
+ var fromCharacter = new VModCharacter(entityManager.GetComponentData(entity), entityManager);
+ var splitEvent = entityManager.GetComponentData(entity);
+ FireInventoryInteractionEvent(entity, fromCharacter, splitEvent.Inventory.GetNetworkedEntity(networkIdToEntityMap)._Entity);
+ }
+ }
+
+ [HarmonyPatch(typeof(SortAllItemsSystem), nameof(SortAllItemsSystem.OnUpdate))]
+ [HarmonyPrefix]
+ private static void SortAllItems(SplitItemSystem __instance)
+ {
+ if(!VWorld.IsServer || __instance._Query == null)
+ {
+ return;
+ }
+
+ var server = VWorld.Server;
+ var entityManager = server.EntityManager;
+ var networkIdToEntityMap = __instance._NetworkIdSystem._NetworkIdToEntityMap;
+
+ var entities = __instance._Query.ToEntityArray(Allocator.Temp);
+ foreach(var entity in entities)
+ {
+ var fromCharacter = new VModCharacter(entityManager.GetComponentData(entity), entityManager);
+ var sortEvent = entityManager.GetComponentData(entity);
+ FireInventoryInteractionEvent(entity, fromCharacter, sortEvent.Inventory.GetNetworkedEntity(networkIdToEntityMap)._Entity);
+ }
+ }
+
+ [HarmonyPatch(typeof(SmartMergeItemsBetweenInventoriesSystem), nameof(SmartMergeItemsBetweenInventoriesSystem.OnUpdate))]
+ [HarmonyPrefix]
+ private static void SmartMergeItemsBetweenInventories(SplitItemSystem __instance)
+ {
+ if(!VWorld.IsServer || __instance._Query == null)
+ {
+ return;
+ }
+
+ var server = VWorld.Server;
+ var entityManager = server.EntityManager;
+ var networkIdToEntityMap = __instance._NetworkIdSystem._NetworkIdToEntityMap;
+
+ var entities = __instance._Query.ToEntityArray(Allocator.Temp);
+ foreach(var entity in entities)
+ {
+ var fromCharacter = new VModCharacter(entityManager.GetComponentData(entity), entityManager);
+ var mergeEvent = entityManager.GetComponentData(entity);
+ FireInventoryInteractionEvent(entity, fromCharacter, mergeEvent.FromInventory.GetNetworkedEntity(networkIdToEntityMap)._Entity, mergeEvent.ToInventory.GetNetworkedEntity(networkIdToEntityMap)._Entity);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/ChestPvPProtection/Plugin.cs b/ChestPvPProtection/Plugin.cs
new file mode 100644
index 0000000..6feb577
--- /dev/null
+++ b/ChestPvPProtection/Plugin.cs
@@ -0,0 +1,56 @@
+using BepInEx;
+using BepInEx.IL2CPP;
+using HarmonyLib;
+using System.Reflection;
+using VMods.Shared;
+using Wetstone.API;
+
+namespace VMods.ChestPvPProtection
+{
+ [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
+ [BepInDependency("xyz.molenzwiebel.wetstone")]
+ [Reloadable]
+ public class Plugin : BasePlugin
+ {
+ #region Variables
+
+ private Harmony _hooks;
+
+ #endregion
+
+ #region Public Methods
+
+ public sealed override void Load()
+ {
+ if(VWorld.IsClient)
+ {
+ Log.LogMessage($"{PluginInfo.PLUGIN_NAME} only needs to be installed server side.");
+ return;
+ }
+ Utils.Initialize(Log, PluginInfo.PLUGIN_NAME);
+
+ ChestPvPProtectionSystemConfig.Initialize(Config);
+
+ ChestPvPProtectionSystem.Initialize();
+
+ _hooks = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly());
+
+ Log.LogInfo($"Plugin {PluginInfo.PLUGIN_NAME} (v{PluginInfo.PLUGIN_VERSION}) is loaded!");
+ }
+
+ public sealed override bool Unload()
+ {
+ if(VWorld.IsClient)
+ {
+ return true;
+ }
+ _hooks?.UnpatchSelf();
+ ChestPvPProtectionSystem.Deinitialize();
+ Config.Clear();
+ Utils.Deinitialize();
+ return true;
+ }
+
+ #endregion
+ }
+}
diff --git a/ChestPvPProtection/Systems/ChestPvPProtectionSystem.cs b/ChestPvPProtection/Systems/ChestPvPProtectionSystem.cs
new file mode 100644
index 0000000..3622624
--- /dev/null
+++ b/ChestPvPProtection/Systems/ChestPvPProtectionSystem.cs
@@ -0,0 +1,76 @@
+using ProjectM;
+using ProjectM.Network;
+using ProjectM.Scripting;
+using System.Linq;
+using Unity.Entities;
+using VMods.Shared;
+using Wetstone.API;
+
+namespace VMods.ChestPvPProtection
+{
+ public static class ChestPvPProtectionSystem
+ {
+ #region Public Methods
+
+ public static void Initialize()
+ {
+ InventoryHooks.InventoryInteractionEvent += OnInventoryInteraction;
+ }
+
+ public static void Deinitialize()
+ {
+ InventoryHooks.InventoryInteractionEvent -= OnInventoryInteraction;
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private static void OnInventoryInteraction(Entity eventEntity, VModCharacter vmodCharacter, params Entity[] inventoryEntities)
+ {
+ if(!ChestPvPProtectionSystemConfig.ChestPvPProtectionEnabled.Value)
+ {
+ return;
+ }
+
+ var server = VWorld.Server;
+ var entityManager = server.EntityManager;
+ var teamChecker = server.GetExistingSystem()._ServerGameManager._TeamChecker;
+
+ if(!vmodCharacter.HasBuff(Utils.PvPProtectionBuff) || vmodCharacter.User.IsAdmin)
+ {
+ return;
+ }
+
+ var playerTeam = entityManager.GetComponentData(vmodCharacter.FromCharacter.Character);
+ var teams = inventoryEntities.Select(FindTeam);
+
+ // Check if the player is not an ally and the inventory isn't neutral (i.e. an npc chest) of any inventory
+ if(teams.Any(team => !teamChecker.IsAllies(playerTeam, team) && !teamChecker.IsNeutral(team)))
+ {
+ entityManager.AddComponent(eventEntity);
+ if(ChestPvPProtectionSystemConfig.ChestPvPProtectionSendMessage.Value)
+ {
+ Utils.SendMessage(vmodCharacter.FromCharacter.User, $"Cannot interact with enemy Chests/Workstations while PvP Protected", ServerChatMessageType.System);
+ }
+ }
+
+ // Nested Method(s)
+ Team FindTeam(Entity entity)
+ {
+ if(entityManager.HasComponent(entity))
+ {
+ return entityManager.GetComponentData(entity);
+ }
+ else
+ {
+ // Workstations don't have a team but are attached, thus retrieve the team of the station's parent.
+ var attach = entityManager.GetComponentData(entity);
+ return entityManager.GetComponentData(attach.Parent);
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/README.md b/README.md
index af50030..922b959 100644
--- a/README.md
+++ b/README.md
@@ -101,3 +101,8 @@ Both the amount of offenses before being punishes as well as the punishment itse
* % of reduced Spell Power
+
+## Chest PvP Protection
+A server-side only mod that prevents looting of enemy player chests/workstations by players with the PvP Protection buff.
+
+Besides looting, it'll also prevent moving, sorting, swapping and merging ("compilsively count") of items within those chests/workstations.
diff --git a/Shared/Utils.cs b/Shared/Utils.cs
index 7599f9e..75114d5 100644
--- a/Shared/Utils.cs
+++ b/Shared/Utils.cs
@@ -20,6 +20,8 @@ public static class Utils
public static readonly PrefabGUID SevereGarlicDebuff = new(1582196539);
+ public static readonly PrefabGUID PvPProtectionBuff = new(1111481396);
+
#endregion
#region Variables
@@ -291,6 +293,17 @@ public static void LogAllComponentTypes(Entity entity, EntityManager? entityMana
Logger.LogMessage($"---");
}
+ public static void LogAllComponentTypes(EntityQuery entityQuery)
+ {
+ var types = entityQuery.GetQueryTypes();
+ Logger.LogMessage($"---");
+ foreach(var t in types)
+ {
+ Logger.LogMessage($"Query Component Type: {t}");
+ }
+ Logger.LogMessage($"---");
+ }
+
#endregion
#region Nested
diff --git a/Shared/VModCharacter.cs b/Shared/VModCharacter.cs
index 866e6f0..d705c71 100644
--- a/Shared/VModCharacter.cs
+++ b/Shared/VModCharacter.cs
@@ -23,6 +23,14 @@ public readonly struct VModCharacter
Character = user.LocalCharacter._Entity,
});
+ public VModCharacter(FromCharacter fromCharacter, EntityManager? entityManager = null)
+ {
+ entityManager ??= Utils.CurrentWorld.EntityManager;
+ User = entityManager.Value.GetComponentData(fromCharacter.User);
+ Character = entityManager.Value.GetComponentData(fromCharacter.Character);
+ FromCharacter = fromCharacter;
+ }
+
public VModCharacter(Entity userEntity, Entity charEntity, EntityManager? entityManager = null)
{
entityManager ??= Utils.CurrentWorld.EntityManager;
diff --git a/Thunderstone/ChestPvPProtection/README.md b/Thunderstone/ChestPvPProtection/README.md
new file mode 100644
index 0000000..a2e60cd
--- /dev/null
+++ b/Thunderstone/ChestPvPProtection/README.md
@@ -0,0 +1,25 @@
+# Chest PvP Protection
+A server-side only mod that prevents looting of enemy player chests/workstations by players with the PvP Protection buff.
+
+Besides looting, it'll also prevent moving, sorting, swapping and merging ("compilsively count") of items within those chests/workstations.
+
+## How to manually install
+* Install [BepInEx](https://v-rising.thunderstore.io/package/BepInEx/BepInExPack_V_Rising/)
+* Install [Wetstone](https://v-rising.thunderstore.io/package/molenzwiebel/Wetstone/)
+* (Locally hosted games only) Install [ServerLaunchFix](https://v-rising.thunderstore.io/package/Mythic/ServerLaunchFix/)
+* Extract the Vmods._mod-name_.dll
+* Move the desired mod(s) to the `[VRising (server) folder]/BepInEx/WetstonePlugins/`
+* Launch the server (or game) to auto-generate the config files
+* Edit the configs as you desire (found in `[VRising (server) folder]/BepInEx/config/`)
+* Reload the mods using the Wetstone commands (by default F6 for client-side mods, and/or `!reload` for server-side mods)
+ * If this doesn't work, or isn't enabled, restart the server/game
+
+## Commands
+Most of the VMods come with a set of commands that can be used. To see the available commands, by default a player or admin can use `!help`.
+Normal players won't see the Admin-only commands listed.
+The prefix (`!`) can be changed on a per-mod basis.
+To prevent spam/abuse there's also a command cooldown for non-admins, this value can also be tweaked on a per-mod basis.
+Commands can also be disabled completely on a per-mod basis.
+
+## More Details
+* [ChangeLog](https://github.com/WhiteFang5/VMods/blob/master/CHANGELOG.md#chest-pvp-protection)
diff --git a/Thunderstone/ChestPvPProtection/manifest.json b/Thunderstone/ChestPvPProtection/manifest.json
new file mode 100644
index 0000000..c6f1a92
--- /dev/null
+++ b/Thunderstone/ChestPvPProtection/manifest.json
@@ -0,0 +1,10 @@
+{
+ "name": "VMods_Chest_PvP_Protection",
+ "description": "A mod that that prevents looting of enemy player chests/workstations by players with the PvP Protection buff.",
+ "version_number": "1.0.0",
+ "dependencies": [
+ "BepInEx-BepInExPack_V_Rising-1.0.0",
+ "molenzwiebel-Wetstone-1.1.0"
+ ],
+ "website_url": "https://github.com/WhiteFang5/VMods#chest-pvp-protection"
+}
diff --git a/VMods.sln b/VMods.sln
index 424c735..985f2bd 100644
--- a/VMods.sln
+++ b/VMods.sln
@@ -11,11 +11,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RecoverEmptyContainers", "R
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExperimentalMod", "ExperimentalMod\ExperimentalMod.csproj", "{695DC4E6-1831-4826-9A64-B85107AB555A}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PvPPunishment", "PvPPunishment\PvPPunishment.csproj", "{FBB60595-8F29-49D5-A9A1-0CEA1B9CFF91}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PvPPunishment", "PvPPunishment\PvPPunishment.csproj", "{FBB60595-8F29-49D5-A9A1-0CEA1B9CFF91}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "Shared\Shared.csproj", "{81DE42A0-8493-49B4-9F0E-353198C1E2AD}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csproj", "{81DE42A0-8493-49B4-9F0E-353198C1E2AD}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PvPLeaderboard", "PvPLeaderboard\PvPLeaderboard.csproj", "{8D3A6ED6-46CD-41CD-BE61-5587F05EAB4E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PvPLeaderboard", "PvPLeaderboard\PvPLeaderboard.csproj", "{8D3A6ED6-46CD-41CD-BE61-5587F05EAB4E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChestPvPProtection", "ChestPvPProtection\ChestPvPProtection.csproj", "{081DA46E-5AAB-4825-89E8-8B167988CC20}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -51,6 +53,10 @@ Global
{8D3A6ED6-46CD-41CD-BE61-5587F05EAB4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D3A6ED6-46CD-41CD-BE61-5587F05EAB4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D3A6ED6-46CD-41CD-BE61-5587F05EAB4E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {081DA46E-5AAB-4825-89E8-8B167988CC20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {081DA46E-5AAB-4825-89E8-8B167988CC20}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {081DA46E-5AAB-4825-89E8-8B167988CC20}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {081DA46E-5AAB-4825-89E8-8B167988CC20}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE