Skip to content

Commit

Permalink
- safer handling for some potential crash causes
Browse files Browse the repository at this point in the history
- familiars returned to player at end of combat if distance >25f
- command to set unarmed spells can be used on players in entered radius
  • Loading branch information
mfoltz committed Nov 20, 2024
1 parent dbbbe1f commit c3f638e
Show file tree
Hide file tree
Showing 19 changed files with 413 additions and 410 deletions.
123 changes: 25 additions & 98 deletions Commands/MiscCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
using Stunlock.Core;
using Unity.Entities;
using VampireCommandFramework;
using static Bloodcraft.Services.DataService.PlayerDictionaries;
using static Bloodcraft.Services.PlayerService;
using static VCF.Core.Basics.RoleCommands;
using User = ProjectM.Network.User;

namespace Bloodcraft.Commands;

internal static class MiscCommands
{
static EntityManager EntityManager => Core.EntityManager;
Expand Down Expand Up @@ -84,9 +84,18 @@ public static void KitMe(ChatCommandContext ctx)
ServerGameManager.TryAddInventoryItem(character, item.Key, item.Value);
}

string kitItems = KitPrefabs.Select(x => $"<color=#ffd9eb>{x.Key.GetPrefabName()}</color>")
.Aggregate((x, y) => $"{x}, <color=#ffd9eb>{y}</color>");
LocalizationService.HandleReply(ctx, $"You've received a starting kit with {kitItems}!");
List<string> kitItems = KitPrefabs.Select(x => $"<color=white>{x.Key.GetPrefabName()}</color>").ToList();

LocalizationService.HandleReply(ctx, $"You've received a starting kit with:");

const int maxPerMessage = 6;
for (int i = 0; i < kitItems.Count; i += maxPerMessage)
{
var batch = kitItems.Skip(i).Take(maxPerMessage);
string items = string.Join(", ", batch);

LocalizationService.HandleReply(ctx, $"{items}");
}
}
else
{
Expand Down Expand Up @@ -258,25 +267,26 @@ public static void CleanUpFams(ChatCommandContext ctx)
});

int counter = 0;

try
{
Dictionary<ulong, (Entity Familiar, int FamKey)> FamiliarActives = new(familiarActives);
List<Entity> dismissedFamiliars = familiarActives.Values.Select(x => x.Familiar).ToList();
Dictionary<ulong, (Entity Familiar, int FamKey)> FamiliarActives = new(DataService.PlayerDictionaries.familiarActives);
List<Entity> dismissedFamiliars = FamiliarActives.Values.Select(x => x.Familiar).ToList();

IEnumerable<Entity> disabledFamiliars = EntityUtilities.GetEntitiesEnumerable(familiarsQuery); // need to filter for active/dismissed familiars and not destroy them
foreach (Entity entity in disabledFamiliars)
{
if (dismissedFamiliars.Contains(entity)) continue;
else
{
if (entity.GetTeamEntity().Has<UserTeam>() && entity.ReadBuffer<DropTableBuffer>()[0].DropTrigger.Equals(DropTriggerType.OnSalvageDestroy))
if (entity.TryGetTeamEntity(out Entity teamEntity) && teamEntity.Has<UserTeam>() && entity.TryGetBuffer<DropTableBuffer>(out var buffer) && buffer[0].DropTrigger.Equals(DropTriggerType.OnSalvageDestroy))
{
if (entity.Has<Disabled>())
{
entity.Remove<Disabled>();
if (entity.Has<DisableWhenNoPlayersInRange>()) entity.Remove<DisableWhenNoPlayersInRange>();
if (entity.Has<DisabledDueToNoPlayersInRange>()) entity.Remove<DisabledDueToNoPlayersInRange>();
if (entity.Has<DisabledDueToNoPlayersInRange>()) entity.Remove<DisabledDueToNoPlayersInRange>();

EntityManager.DestroyEntity(entity);
counter++;
}
Expand All @@ -297,6 +307,7 @@ public static void CleanUpFams(ChatCommandContext ctx)
});

counter = 0;

try
{
IEnumerable<Entity> networkedSequences = EntityUtilities.GetEntitiesEnumerable(networkedSequencesQuery);
Expand All @@ -312,6 +323,10 @@ public static void CleanUpFams(ChatCommandContext ctx)

if (secondaryTarget.TryGetComponent(out PrefabGUID secondaryTargetPrefab) && secondaryTarget.Has<BlockFeedBuff>())
{
if (secondaryTarget.Has<Disabled>()) secondaryTarget.Remove<Disabled>();
if (secondaryTarget.Has<DisableWhenNoPlayersInRange>()) secondaryTarget.Remove<DisableWhenNoPlayersInRange>();
if (secondaryTarget.Has<DisabledDueToNoPlayersInRange>()) secondaryTarget.Remove<DisabledDueToNoPlayersInRange>();

DestroyUtility.Destroy(EntityManager, secondaryTarget, DestroyDebugReason.None);
counter++;
}
Expand Down Expand Up @@ -374,92 +389,4 @@ public static void CleanUpFams(ChatCommandContext ctx)

LocalizationService.HandleReply(ctx, $"Destroyed <color=white>{counter}</color> entities found in player FollowerBuffers and MinionBuffers...");
}

/*
[Command(name: "switcheroo", adminOnly: true, usage: ".switch [OriginalPlayer] [NewPlayer]", description: "Swaps the steamIDs of two players for testing.")] // this is just swapplayers without kicking people to use their mod data, ty Odjit <3 don't feel like finding out if it works like I think it will right now so commenting out >_>
public static void SwitchPlayers(ChatCommandContext ctx, string originalPlayer, string newPlayer)
{
if (originalPlayer.TryGetPlayerInfo(out PlayerInfo originalPlayerInfo) && newPlayer.TryGetPlayerInfo(out PlayerInfo newPlayerInfo))
{
Entity originalUserEntity = originalPlayerInfo.UserEntity;
Entity newUserEntity = newPlayerInfo.UserEntity;
User originalUser = originalUserEntity.Read<User>();
User newUser = newUserEntity.Read<User>();
(originalUser.PlatformId, newUser.PlatformId) = (newUser.PlatformId, originalUser.PlatformId);
originalUserEntity.Write(originalUser);
newUserEntity.Write(newUser);
ctx.Reply($"Switched steamIds for {originalPlayerInfo.User.CharacterName} with {newPlayerInfo.User.CharacterName}!");
}
}
[Command(name: "bloblog", shortHand:"blob", adminOnly: true, usage: ".blob [PrefabGUID]", description: "BlobString testing.")]
public static void BlobStringLogCommand(ChatCommandContext ctx, int guidHash)
{
if (!Core.SystemService.PrefabCollectionSystem._PrefabGuidToEntityMap.TryGetValue(new(guidHash), out Entity prefabEntity))
{
ctx.Reply("Couldn't find prefab...");
return;
}
else if (prefabEntity.TryGetComponent(out AbilityCastCondition castCondition))
{
unsafe
{
BlobAssetReference<ConditionBlob> blobAssetReference = castCondition.Condition;
ConditionBlob* conditionBlob = (ConditionBlob*)blobAssetReference.GetUnsafePtr();
ConditionInfo conditionInfo = conditionBlob->Info;
ReadBlobString(ref conditionInfo);
}
}
else
{
ctx.Reply("AbilityCastCondition not found on prefab entity...");
}
}
unsafe static void ReadBlobString(ref ConditionInfo conditionInfo)
{
// Get a pointer to the ConditionInfo structure
fixed (ConditionInfo* conditionInfoPtr = &conditionInfo)
{
// Get the pointer to the Prefab BlobString
BlobString* prefabBlobStringPtr = &conditionInfoPtr->Prefab;
// Read the Prefab string
string prefabName = ParseBlobString(prefabBlobStringPtr);
// Get the pointer to the Component BlobString
BlobString* componentBlobStringPtr = &conditionInfoPtr->Component;
// Read the Component string
string componentName = ParseBlobString(componentBlobStringPtr);
// Now you can log or use the strings as needed
Core.Log.LogInfo($"Prefab: {prefabName}");
Core.Log.LogInfo($"Component: {componentName}");
}
}
unsafe static string ParseBlobString(BlobString* blobStringPtr)
{
// Get a pointer to the BlobArray<byte> Data field
BlobArray<byte>* dataPtr = &blobStringPtr->Data;
// Get the base pointer, which is the address of the m_OffsetPtr field
byte* basePtr = (byte*)&dataPtr->m_OffsetPtr;
// Compute the data pointer using the offset
byte* bytes = basePtr + dataPtr->m_OffsetPtr;
// Read the length from the m_Length field
int length = dataPtr->m_Length;
// Convert the bytes to a string using UTF8 encoding
string result = BlobString.ToString(bytes, length);
return result;
}
*/
}
}
84 changes: 67 additions & 17 deletions Commands/WeaponCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@
using ProjectM;
using ProjectM.Scripting;
using ProjectM.Shared;
using Steamworks;
using Stunlock.Core;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine.TextCore.Text;
using VampireCommandFramework;
using static Bloodcraft.Services.PlayerService;
using static Bloodcraft.Systems.Expertise.WeaponManager;
Expand Down Expand Up @@ -260,42 +265,87 @@ public static void ListWeaponsCommand(ChatCommandContext ctx)
LocalizationService.HandleReply(ctx, $"Available Weapon Expertises: <color=#c0c0c0>{weaponTypes}</color>");
}

[Command(name: "setspells", shortHand: "spell", adminOnly: true, usage: ".wep spell [Name] [Slot] [PrefabGUID]", description: "Manually sets spells for testing.")]
public static void SetSpellCommand(ChatCommandContext ctx, string name, int slot, int ability)
[Command(name: "setspells", shortHand: "spell", adminOnly: true, usage: ".wep spell [Name] [Slot] [PrefabGUID] [Radius]", description: "Manually sets spells for testing (if you enter a radius it will apply to players around the entered name).")]
public static void SetSpellCommand(ChatCommandContext ctx, string name, int slot, int ability, float radius = 0f)
{
if (!ConfigService.UnarmedSlots)
{
LocalizationService.HandleReply(ctx, "Extra spell slots are not enabled.");
return;
}

if (slot < 1 || slot > 2)
{
LocalizationService.HandleReply(ctx, "Invalid slot.");
LocalizationService.HandleReply(ctx, "Invalid slot (<color=white>1</color> for Q or <color=white>2</color> for E)");
return;
}

PlayerInfo playerInfo = PlayerCache.FirstOrDefault(kvp => kvp.Key.ToLower() == name.ToLower()).Value;
if (!playerInfo.UserEntity.Exists())
if (radius > 0f)
{
ctx.Reply($"Couldn't find player.");
return;
}
Entity character = ctx.Event.SenderCharacterEntity;
float3 charPosition = character.Read<Translation>().Value;

HashSet<PlayerInfo> processed = [];
Dictionary<string, PlayerInfo> players = new(OnlineCache);

ulong SteamID = playerInfo.User.PlatformId;
foreach (PlayerInfo playerInfo in players.Values)
{
if (processed.Contains(playerInfo)) continue;
else if (playerInfo.CharEntity.TryGetComponent(out Translation translation) && math.distance(charPosition, translation.Value) <= radius)
{
ulong steamId = playerInfo.User.PlatformId;

if (SteamID.TryGetPlayerSpells(out var spells))
if (steamId.TryGetPlayerSpells(out var spells))
{
if (slot == 1)
{
spells.FirstUnarmed = ability;
LocalizationService.HandleReply(ctx, $"First unarmed slot set to <color=white>{new PrefabGUID(ability).LookupName()}</color> for <color=green>{playerInfo.User.CharacterName.Value}</color>.");
}
else if (slot == 2)
{
spells.SecondUnarmed = ability;
LocalizationService.HandleReply(ctx, $"Second unarmed slot set to <color=white>{new PrefabGUID(ability).LookupName()}</color> for <color=green>{playerInfo.User.CharacterName.Value}</color>.");
}

steamId.SetPlayerSpells(spells);
}

processed.Add(playerInfo);
}
}
}
else if (radius < 0f)
{
if (slot == 1)
LocalizationService.HandleReply(ctx, "Radius must be positive if entering a value!");
return;
}
else
{
PlayerInfo playerInfo = PlayerCache.FirstOrDefault(kvp => kvp.Key.ToLower() == name.ToLower()).Value;
if (!playerInfo.UserEntity.Exists())
{
spells.FirstUnarmed = ability;
LocalizationService.HandleReply(ctx, $"First unarmed slot set to <color=white>{new PrefabGUID(ability).LookupName()}</color> for <color=green>{playerInfo.User.CharacterName.Value}</color>.");
ctx.Reply($"Couldn't find player.");
return;
}
else

ulong steamId = playerInfo.User.PlatformId;

if (steamId.TryGetPlayerSpells(out var spells))
{
spells.SecondUnarmed = ability;
LocalizationService.HandleReply(ctx, $"Second unarmed slot set to <color=white>{new PrefabGUID(ability).LookupName()}</color> for <color=green>{playerInfo.User.CharacterName.Value}</color>.");
if (slot == 1)
{
spells.FirstUnarmed = ability;
LocalizationService.HandleReply(ctx, $"First unarmed slot set to <color=white>{new PrefabGUID(ability).LookupName()}</color> for <color=green>{playerInfo.User.CharacterName.Value}</color>.");
}
else if (slot == 2)
{
spells.SecondUnarmed = ability;
LocalizationService.HandleReply(ctx, $"Second unarmed slot set to <color=white>{new PrefabGUID(ability).LookupName()}</color> for <color=green>{playerInfo.User.CharacterName.Value}</color>.");
}

steamId.SetPlayerSpells(spells);
}
SteamID.SetPlayerSpells(spells);
}
}

Expand Down
Loading

0 comments on commit c3f638e

Please sign in to comment.