Skip to content

Commit

Permalink
Significantly improve client and server side performance.
Browse files Browse the repository at this point in the history
Introduces caches at busy places.
  • Loading branch information
rubensworks committed Dec 18, 2014
1 parent 9e3b679 commit 4c783e2
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 42 deletions.
41 changes: 41 additions & 0 deletions src/main/java/evilcraft/core/algorithm/SingleCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package evilcraft.core.algorithm;

/**
* A generic single object cache.
* @param <K> The key type.
* @param <V> The value type.
* @author rubensworks
*/
public class SingleCache<K, V> {

private boolean initialized = false;
private K key = null;
private V value = null;
private ICacheUpdater<K, V> updater;

public SingleCache(ICacheUpdater<K, V> updater) {
this.updater = updater;
}

public V get(K key) {
if(!this.initialized || !this.updater.isKeyEqual(this.key, key)) {
this.value = this.updater.getNewValue(key);
this.key = key;
this.initialized = true;
}
return this.value;
}

/**
* This is responsible for fetching new updates when the cache desires this.
* @param <K> The key type.
* @param <V> The value type.
*/
public static interface ICacheUpdater<K, V> {

public V getNewValue(K key);
public boolean isKeyEqual(K cacheKey, K newKey);

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@ public boolean equals(Object object) {
if (!(object instanceof ItemStackRecipeComponent)) return false;
ItemStackRecipeComponent that = (ItemStackRecipeComponent)object;


for(ItemStack itemStack : getItemStacks()) {
if(equals(itemStack, that.getItemStack())) {
return true;
// To increase performance, first check if the comparing stack is not null before
// potentially matching it with the whole oredict.
if(that.getItemStack() != null) {
for (ItemStack itemStack : getItemStacks()) {
if (equals(itemStack, that.getItemStack())) {
return true;
}
}
}

Expand Down
33 changes: 16 additions & 17 deletions src/main/java/evilcraft/event/LivingUpdateEventHook.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
package evilcraft.event;

import net.minecraft.entity.passive.EntityAnimal;
import net.minecraft.entity.passive.EntityVillager;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import evilcraft.Configs;
Expand All @@ -17,6 +10,13 @@
import evilcraft.core.helper.WorldHelpers;
import evilcraft.entity.monster.Werewolf;
import evilcraft.entity.villager.WerewolfVillagerConfig;
import net.minecraft.entity.passive.EntityAnimal;
import net.minecraft.entity.passive.EntityVillager;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;

/**
* Event hook for {@link LivingUpdateEvent}.
Expand All @@ -43,18 +43,17 @@ public void onLivingUpdate(LivingUpdateEvent event) {

private void dropExcrement(LivingUpdateEvent event) {
if(event.entity instanceof EntityAnimal && Configs.isEnabled(ExcrementPileConfig.class)
&& !event.entity.worldObj.isRemote) {
&& !event.entity.worldObj.isRemote
&& event.entity.worldObj.rand.nextInt(CHANCE_DROP_EXCREMENT) == 0) {
EntityAnimal entity = (EntityAnimal) event.entity;
World world = entity.worldObj;
if(world.rand.nextInt(CHANCE_DROP_EXCREMENT) == 0) {
int x = MathHelper.floor_double(entity.posX);
int y = MathHelper.floor_double(entity.posY);
int z = MathHelper.floor_double(entity.posZ);
if(world.getBlock(x, y, z) == Blocks.air && world.getBlock(x, y - 1, z).isNormalCube()) {
world.setBlock(x, y, z, ExcrementPile.getInstance());
} else if (world.getBlock(x, y, z) == ExcrementPile.getInstance()) {
ExcrementPile.heightenPileAt(world, x, y, z);
}
int x = MathHelper.floor_double(entity.posX);
int y = MathHelper.floor_double(entity.posY);
int z = MathHelper.floor_double(entity.posZ);
if(world.getBlock(x, y, z) == Blocks.air && world.getBlock(x, y - 1, z).isNormalCube()) {
world.setBlock(x, y, z, ExcrementPile.getInstance());
} else if(world.getBlock(x, y, z) == ExcrementPile.getInstance()) {
ExcrementPile.heightenPileAt(world, x, y, z);
}
}
}
Expand Down
64 changes: 54 additions & 10 deletions src/main/java/evilcraft/network/PacketCodec.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package evilcraft.network;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.ClassUtils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import evilcraft.core.algorithm.SingleCache;
import org.apache.commons.lang3.ClassUtils;

import java.lang.reflect.Field;
import java.util.*;

/**
* Packet with automatic coding and decoding of basic fields annotated with {@link CodecField}.
Expand Down Expand Up @@ -142,6 +140,42 @@ public Object decode(ByteArrayDataInput input) {
}
});
}

private SingleCache<Void, List<Field>> fieldCache = new SingleCache<Void, List<Field>>(
new SingleCache.ICacheUpdater<Void, List<Field>>() {

@Override
public List<Field> getNewValue(Void key) {
Field[] fields = PacketCodec.this.getClass().getDeclaredFields();

// Sort this because the Java API tells us that getDeclaredFields()
// does not deterministically define the order of the fields in the array.
// Otherwise we might get nasty class cast exceptions when running in SMP.
Arrays.sort(fields, new Comparator<Field>() {

@Override
public int compare(Field o1, Field o2) {
return o1.getName().compareTo(o2.getName());
}

});

List<Field> fieldList = Lists.newLinkedList();
for(final Field field : fields) {
if(field.isAnnotationPresent(CodecField.class)) {
fieldList.add(field);
}
}

return fieldList;
}

@Override
public boolean isKeyEqual(Void cacheKey, Void newKey) {
return true;
}

});

protected static ICodecAction getAction(Class<?> clazz) {
if(ClassUtils.isPrimitiveWrapper(clazz)) {
Expand All @@ -156,7 +190,7 @@ protected static ICodecAction getAction(Class<?> clazz) {
}

private void loopCodecFields(ICodecRunnable runnable) {
Field[] fields = this.getClass().getDeclaredFields();
/*Field[] fields = this.getClass().getDeclaredFields();
// Sort this because the Java API tells us that getDeclaredFields()
// does not deterministically define the order of the fields in the array.
Expand All @@ -181,7 +215,17 @@ public int compare(Field o1, Field o2) {
runnable.run(field, action);
field.setAccessible(accessible);
}
}
}*/
for(Field field : fieldCache.get(null)) {
Class<?> clazz = field.getType();
ICodecAction action = getAction(clazz);

// Make private fields temporarily accessible.
boolean accessible = field.isAccessible();
field.setAccessible(true);
runnable.run(field, action);
field.setAccessible(accessible);
}
}

@Override
Expand Down
49 changes: 38 additions & 11 deletions src/main/java/evilcraft/tileentity/TileBloodInfuser.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import evilcraft.api.recipes.custom.IRecipe;
import evilcraft.block.BloodInfuser;
import evilcraft.core.algorithm.SingleCache;
import evilcraft.core.fluid.BloodFluidConverter;
import evilcraft.core.fluid.ImplicitFluidConversionTank;
import evilcraft.core.fluid.SingleUseTank;
Expand Down Expand Up @@ -30,6 +31,8 @@
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidContainerItem;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Triple;

import java.util.LinkedHashMap;
import java.util.LinkedList;
Expand Down Expand Up @@ -78,6 +81,8 @@ public class TileBloodInfuser extends TileWorking<TileBloodInfuser, MutableInt>
public static final Fluid ACCEPTED_FLUID = Blood.getInstance();

private int infuseTicker;
private SingleCache<Triple<ItemStack, FluidStack, Integer>,
IRecipe<ItemFluidStackAndTierRecipeComponent, ItemStackRecipeComponent, DurationRecipeProperties>> recipeCache;

private static final Map<Class<?>, ITickAction<TileBloodInfuser>> INFUSE_TICK_ACTIONS = new LinkedHashMap<Class<?>, ITickAction<TileBloodInfuser>>();
static {
Expand Down Expand Up @@ -159,6 +164,35 @@ public void applyUpgrade(TileBloodInfuser upgradable, Upgrades.Upgrade upgrade,
}
}
});

// Efficient cache to retrieve the current craftable recipe.
recipeCache = new SingleCache<Triple<ItemStack, FluidStack, Integer>,
IRecipe<ItemFluidStackAndTierRecipeComponent, ItemStackRecipeComponent, DurationRecipeProperties>>(
new SingleCache.ICacheUpdater<Triple<ItemStack, FluidStack, Integer>,
IRecipe<ItemFluidStackAndTierRecipeComponent, ItemStackRecipeComponent, DurationRecipeProperties>>() {
@Override
public IRecipe<ItemFluidStackAndTierRecipeComponent, ItemStackRecipeComponent, DurationRecipeProperties> getNewValue(Triple<ItemStack, FluidStack, Integer> key) {
ItemFluidStackAndTierRecipeComponent recipeInput = new ItemFluidStackAndTierRecipeComponent(key.getLeft(),
key.getMiddle(), -1);
IRecipe<ItemFluidStackAndTierRecipeComponent, ItemStackRecipeComponent, DurationRecipeProperties> maxRecipe = null;
int maxRecipeTier = -1;
for(IRecipe<ItemFluidStackAndTierRecipeComponent, ItemStackRecipeComponent, DurationRecipeProperties> recipe :
BloodInfuser.getInstance().getRecipeRegistry().findRecipesByInput(recipeInput)) {
if(recipe.getInput().getTier() > maxRecipeTier && key.getRight() >= recipe.getInput().getTier()) {
maxRecipe = recipe;
}
}
return maxRecipe;
}

@Override
public boolean isKeyEqual(Triple<ItemStack, FluidStack, Integer> cacheKey, Triple<ItemStack, FluidStack, Integer> newKey) {
return cacheKey == null || newKey == null ||
(ItemStack.areItemStacksEqual(cacheKey.getLeft(), newKey.getLeft()) &&
FluidStack.areFluidStackTagsEqual(cacheKey.getMiddle(), newKey.getMiddle()) &&
cacheKey.getRight().equals(newKey.getRight()));
}
});
}

@Override
Expand All @@ -173,17 +207,10 @@ protected SingleUseTank newTank(String tankName, int tankSize) {
*/
public IRecipe<ItemFluidStackAndTierRecipeComponent, ItemStackRecipeComponent, DurationRecipeProperties>
getRecipe(ItemStack itemStack) {
ItemFluidStackAndTierRecipeComponent recipeInput = new ItemFluidStackAndTierRecipeComponent(itemStack,
getTank().getFluid(), -1);
IRecipe<ItemFluidStackAndTierRecipeComponent, ItemStackRecipeComponent, DurationRecipeProperties> maxRecipe = null;
int maxRecipeTier = -1;
for(IRecipe<ItemFluidStackAndTierRecipeComponent, ItemStackRecipeComponent, DurationRecipeProperties> recipe :
BloodInfuser.getInstance().getRecipeRegistry().findRecipesByInput(recipeInput)) {
if(recipe.getInput().getTier() > maxRecipeTier && getTier() >= recipe.getInput().getTier()) {
maxRecipe = recipe;
}
}
return maxRecipe;
return recipeCache.get(new ImmutableTriple<ItemStack, FluidStack, Integer>(
itemStack == null ? null : itemStack.copy(),
getTank().getFluid() == null ? null : getTank().getFluid().copy(),
getTier()));
}

@Override
Expand Down

0 comments on commit 4c783e2

Please sign in to comment.