Skip to content

Commit

Permalink
Chopping - Revise deletion of plants (#134)
Browse files Browse the repository at this point in the history
* Move deletion of loadtime entities to a game mode component and use entity IDs instead of positions

* Replicate deleted entity IDs as ints

* Use wrapper instead of ints for EntityID
  • Loading branch information
Kexanone authored Dec 12, 2024
1 parent 011e329 commit 71be006
Show file tree
Hide file tree
Showing 9 changed files with 298 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ class ACE_Chopping_UserAction : ACE_ShovelUserAction
if (!plant)
return;

userCtrl.ACE_DeleteEntityAtPosition(plant.GetOrigin());
SCR_EntityHelper.DeleteEntityAndChildren(helper);
userCtrl.ACE_RequestDeleteEntity(plant);
delete helper;
}

//------------------------------------------------------------------------------------------------
Expand Down
7 changes: 7 additions & 0 deletions addons/core/Prefabs/MP/Modes/GameMode_Base.et
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
SCR_BaseGameMode {
ID "56B2B479C6B96951"
components {
ACE_LoadtimeEntityManager "{62FC19C3B5BE2C5F}" {
}
}
}
17 changes: 17 additions & 0 deletions addons/core/Prefabs/MP/Modes/GameMode_Base.et.meta
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
MetaFileClass {
Name "{0F307326459A1395}Prefabs/MP/Modes/GameMode_Base.et"
Configurations {
EntityTemplateResourceClass PC {
}
EntityTemplateResourceClass XBOX_ONE : PC {
}
EntityTemplateResourceClass XBOX_SERIES : PC {
}
EntityTemplateResourceClass PS4 : PC {
}
EntityTemplateResourceClass PS5 : PC {
}
EntityTemplateResourceClass HEADLESS : PC {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,22 @@
//! Managed by SCR_DSSessionCallback.
class ACE_EditorStruct : SCR_JsonApiStruct
{
// SCR_JsonApiStruct does not support array of PoD, hence we use ACE_VectorStruct as wrapper
protected ref array<ref ACE_VectorStruct> m_aACE_DeletedEntityPositions = {};
protected ref array<string> m_aACE_DeletedEntityIDs = {};

//------------------------------------------------------------------------------------------------
void ACE_EditorStruct()
{
RegV("m_aACE_DeletedEntityPositions");
RegV("m_aACE_DeletedEntityIDs");
}

//------------------------------------------------------------------------------------------------
// Print out contents of saved data.
override void Log()
{
Print("--- ACE_EditorStruct ------------------------");
for (int i = 0, count = m_aACE_DeletedEntityPositions.Count(); i < count; i++)
for (int i = 0, count = m_aACE_DeletedEntityIDs.Count(); i < count; i++)
{
Print("Removed entity position: " + m_aACE_DeletedEntityPositions[i]);
Print("Removed entity with ID: " + m_aACE_DeletedEntityIDs[i]);
}
Print("---------------------------------------------");
}
Expand All @@ -28,47 +27,46 @@ class ACE_EditorStruct : SCR_JsonApiStruct
//! Write world data into the struct.
override bool Serialize()
{

SCR_BaseGameMode gameMode = SCR_BaseGameMode.Cast(GetGame().GetGameMode());
if (!gameMode)
ACE_LoadtimeEntityManager manager = ACE_LoadtimeEntityManager.GetInstance();
if (!manager)
return false;

m_aACE_DeletedEntityPositions.Clear();
array<EntityID> entityIDs = manager.GetDeletedEntityIDs();
m_aACE_DeletedEntityIDs.Clear();
m_aACE_DeletedEntityIDs.Reserve(entityIDs.Count());

foreach (vector pos : gameMode.ACE_GetDeletedEntityPositions())
foreach (EntityID entityID : entityIDs)
{
ACE_VectorStruct posStruct = new ACE_VectorStruct();
posStruct.SetVector(pos);
m_aACE_DeletedEntityPositions.Insert(posStruct);
m_aACE_DeletedEntityIDs.Insert(ACE_EntityIdHelper.ToString(entityID));
}

return true;
}

//------------------------------------------------------------------------------------------------
//! Read data from the struct and apply them in the world.
//! Read data from the struct and apply them to the world.
override bool Deserialize()
{
SCR_BaseGameMode gameMode = SCR_BaseGameMode.Cast(GetGame().GetGameMode());
if (!gameMode)
ACE_LoadtimeEntityManager manager = ACE_LoadtimeEntityManager.GetInstance();
if (!manager)
return false;

array<vector> deletedEntityPositions = {};
array<EntityID> entityIDs = {};
entityIDs.Reserve(m_aACE_DeletedEntityIDs.Count());

foreach (ACE_VectorStruct posStruct : m_aACE_DeletedEntityPositions)
foreach (string str : m_aACE_DeletedEntityIDs)
{
deletedEntityPositions.Insert(posStruct.GetVector());
};

gameMode.ACE_DeleteEntitiesAtPositionsGlobal(deletedEntityPositions);
entityIDs.Insert(ACE_EntityIdHelper.FromString(str));
}

manager.DeleteEntitiesByIdGlobal(entityIDs);
return true;
}

//------------------------------------------------------------------------------------------------
//! Clear cached data.
override void ClearCache()
{
m_aACE_DeletedEntityPositions.Clear();
m_aACE_DeletedEntityIDs.Clear();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//------------------------------------------------------------------------------------------------
class ACE_LoadtimeEntityManagerClass : SCR_BaseGameModeComponentClass
{
}

//------------------------------------------------------------------------------------------------
//! Methods for manipulating unreplicated loadtime entities
class ACE_LoadtimeEntityManager : SCR_BaseGameModeComponent
{
// array<EntityID> can't be properly replicated (see https://feedback.bistudio.com/T186903) so we use a wrapper instead
[RplProp(onRplName: "DeleteInitialEntities")]
protected ref array<ref ACE_EntityIdWrapper> m_aDeletedEntityIDs = {};

protected static ACE_LoadtimeEntityManager s_pInstance;

//------------------------------------------------------------------------------------------------
void ACE_LoadtimeEntityManager(IEntityComponentSource src, IEntity ent, IEntity parent)
{
s_pInstance = this;
}

//------------------------------------------------------------------------------------------------
static ACE_LoadtimeEntityManager GetInstance()
{
return s_pInstance;
}

//------------------------------------------------------------------------------------------------
//! Ensures that already deleted unreplicated entities are deleted for JIPs
void DeleteInitialEntities()
{
foreach (ACE_EntityIdWrapper idWrapper : m_aDeletedEntityIDs)
{
DeleteEntityByIdLocal(idWrapper.GetID());
}
}

//------------------------------------------------------------------------------------------------
//! Deletes unreplicated entities by ID for all machines
//! Can only be called on the server
[RplRpc(RplChannel.Reliable, RplRcver.Server)]
void DeleteEntitiesByIdGlobal(notnull array<EntityID> entityIDs)
{
array<ref ACE_EntityIdWrapper> newIDs = {};
newIDs.Reserve(entityIDs.Count());
m_aDeletedEntityIDs.Reserve(m_aDeletedEntityIDs.Count() + newIDs.Count());

foreach (EntityID entityID : entityIDs)
{
ACE_EntityIdWrapper idWrapper = ACE_EntityIdWrapper.CreateID(entityID);
newIDs.Insert(idWrapper);
m_aDeletedEntityIDs.Insert(idWrapper);
}

Rpc(RpcDo_DeleteEntityByBitsBroadcast, newIDs);
RpcDo_DeleteEntityByBitsBroadcast(newIDs);
}

//------------------------------------------------------------------------------------------------
[RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
protected void RpcDo_DeleteEntityByBitsBroadcast(array<ref ACE_EntityIdWrapper> newIDs)
{
foreach (ACE_EntityIdWrapper idWrapper : newIDs)
{
DeleteEntityByIdLocal(idWrapper.GetID());
}
}

//------------------------------------------------------------------------------------------------
//! Deletes unreplicated entity by ID on a local machine
void DeleteEntityByIdLocal(EntityID entityID)
{
IEntity entity = GetGame().GetWorld().FindEntityByID(entityID);
if (entity)
delete entity;
}

//------------------------------------------------------------------------------------------------
//! Return all deleted unreplicated entity IDs
//! Can only be called on the server
array<EntityID> GetDeletedEntityIDs()
{
array<EntityID> entityIDs = {};
entityIDs.Reserve(m_aDeletedEntityIDs.Count());

foreach (ACE_EntityIdWrapper idWrapper : m_aDeletedEntityIDs)
{
entityIDs.Insert(idWrapper.GetID());
}

return entityIDs;
}
}

//------------------------------------------------------------------------------------------------
//! Temporary workaround for https://feedback.bistudio.com/T186903
class ACE_EntityIdWrapper : Managed
{
static const int SNAPSHOT_SIZE_VALUE = 8; //--- EntityID is 64 bit integer
protected EntityID m_iID;

//------------------------------------------------------------------------------------------------
static ACE_EntityIdWrapper CreateID(EntityID id)
{
ACE_EntityIdWrapper instance = new ACE_EntityIdWrapper();
instance.m_iID = id;
return instance;
}

//------------------------------------------------------------------------------------------------
EntityID GetID()
{
return m_iID;
}

//------------------------------------------------------------------------------------------------
static void Encode(SSnapSerializerBase snapshot, ScriptCtx hint, ScriptBitSerializer packet)
{
snapshot.Serialize(packet, SNAPSHOT_SIZE_VALUE);
}

//------------------------------------------------------------------------------------------------
static bool Decode(ScriptBitSerializer packet, ScriptCtx hint, SSnapSerializerBase snapshot)
{
return snapshot.Serialize(packet, SNAPSHOT_SIZE_VALUE);
}

//------------------------------------------------------------------------------------------------
static bool SnapCompare(SSnapSerializerBase lhs, SSnapSerializerBase rhs, ScriptCtx hint)
{
return lhs.CompareSnapshots(rhs, SNAPSHOT_SIZE_VALUE);
}

//------------------------------------------------------------------------------------------------
static bool PropCompare(ACE_EntityIdWrapper prop, SSnapSerializerBase snapshot, ScriptCtx hint)
{
return snapshot.Compare(prop.m_iID, SNAPSHOT_SIZE_VALUE);
}

//------------------------------------------------------------------------------------------------
static bool Extract(ACE_EntityIdWrapper prop, ScriptCtx hint, SSnapSerializerBase snapshot)
{
snapshot.SerializeBytes(prop.m_iID, SNAPSHOT_SIZE_VALUE);
return true;
}

//------------------------------------------------------------------------------------------------
static bool Inject(SSnapSerializerBase snapshot, ScriptCtx hint, ACE_EntityIdWrapper prop)
{
return Extract(prop, hint, snapshot);
}
}
48 changes: 0 additions & 48 deletions addons/core/scripts/Game/ACE_Core/GameMode/SCR_BaseGameMode.c

This file was deleted.

26 changes: 26 additions & 0 deletions addons/core/scripts/Game/ACE_Core/Global/ACE_EntityIdHelper.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------------------------
class ACE_EntityIdHelper
{
//------------------------------------------------------------------------------------------------
static array<int> ToInt(EntityID id)
{
return ACE_HexTools.HexStringToInt(ToString(id));
}

//------------------------------------------------------------------------------------------------
static EntityID FromString(string str)
{
array<int> bits = ACE_HexTools.HexStringToInt(str);
if (bits.Count() < 2)
return EntityID.INVALID;

return EntityID.FromInt(bits[0], bits[1]);
}

//------------------------------------------------------------------------------------------------
static string ToString(EntityID id)
{
// Drop the last three characters, which are " {}"
return id.ToString().Substring(0, 18);
}
}
Loading

0 comments on commit 71be006

Please sign in to comment.