Entity Cache

This package is similar to Object Pooling, except instead of generating a large pool of 1 entity, we’re creating 1 instance of many entities.

Why would you want to do this?

Imagine your game is doing a lot of logic on the local client. You want to spawn an entity and run some functions on it, but you can’t because you would need to spawn the object on the server, then send the instance to the client to operate on. Due to network latency, the object isn’t available on the client by the time the message arrives.

This is the use-case Entity Cache looks to solve.

Usage

  • Install the Entity Cache package
  • Add the Entity Cache locator to the world
  • Add all templates you want to be able to reference later on in the game as a child of this locator.
    • You can (and should) sub-divide templates by their usage under intermediate locators. Entity Cache will find them regardless.

Example setup for Critter Catchers:

image

In this screenshot we have 1 entity cache with 2 child locators. Under each of those locators are the templates we want to use throughout our game.

To get a reference to any of these entities, run the code

entityCache:FindEntityByTemplate(template)

FindEntityByTemplate accepts 1 argument, which is either the name of a template as a string, or a template reference itself.

It returns the entity in the cache that belongs to that template.

If a script is using the cache often, you should get a reference to it once in an Init function like so

self.cache = GetWorld():FindScript("entityCacheScript")

Example

In Critter Catchers, when you start a random encounter, we use a Loot Table to get a template for the encounter.

We pass that template, along with the level the monster should be, based on the area you’re in, to the client. From there, we can safely modify the monster instance just for that user.


# Get a reference to the existing entity from the cache
local mon = self:GetCache():FindEntityByTemplate(t.template).monsterScript

# Create stat variations for wild encounters
if wildEncounter then 
  mon:SeedIvs()				
end

-- Set the monster's level
mon:SetLevel(t.level)

-- Recompute stats based on the new level
mon:ComputeStats()

-- Heal the monster (It could be damaged from the last time the player encountered it)
mon:ResetHp()

-- Sanitize monster state
mon:InitForBattle()

mon.properties.isWild = wildEncounter

From the server’s perspective, it’s holding completely static entities that aren’t networked at all. The user will never see the server side monster, will never interact with it, it will never move.

From the client’s perspective, though, we have a suite of scripts we can run safely modify properties and call functions on without worry of disrupting another player’s gameplay.

Example 2

If you’ll notice from the previous screenshot, we also cache the items in Critter Catchers. Items in a player’s inventory do not exist, they’re simply stored as a template name and a quantity. If the item (like a potion) has a script on it that defines what happens when you use it, we would need to spawn that item, run the script, then destroy it. This is quite expensive on the server, and introduces latency as we move data from the server to the client.

Instead, we modify the cMenuInventoryScript to check if a cache exists. If it does, we get a reference to the existing entity, and use it directly.

-- snip
if self.cache then 
	entity = self.cache:FindEntityByTemplate(templateName)
end 

-- snip

entity.cMenuInventoryItemSpecScript:OnUse(self.user)
1 Like