Jump to content

Tick call back efficency


jordan30001

Recommended Posts

Pre Note: Prepare for huge wall of text.

 

I came across a major bottleneck when implementing certain features into my mod

 

here is the basic gist of what I am doing and wanted some input if your a player/server owner or other inputs.

 

Lets say on a server there are 10 players

each player is queued to drop 64*100 items each so each player drops 6400 items

so 10 players would drop 64,000 items

 

I thought about dropping the items individually but obviously on single player or multiplayer would grind to a halt if all these items were drooped at once.

 

So just to make this less of a strain it doesn't drop individual items and will do its best to drop the maximum amount of items in one stack

 

so rather than dropping 64,000 individual items it drops 6400 itemstacks still would probably cripple most games.

 

I then thought well if I made a list of items to drop and then for each stack of 64 items it would drop in 10 ticks from its previous player (ticks is based on player not globably)

 

so when dropping 6400 itemstacks it would take 1000 ticks over 50 seconds

 

obviously this is still very intensive on a server over 50 seconds but its better than dropping them all in one tick

 

an expansion to this is that I keep a global on how many global items will drop in the next x ticks and change the tickrate (will get onto this in a second) of the items to be dropped increasing in 1 tick when certain parameters are met.

 

I then came into yet another problem to check when an itemstack needs to tick

it would have to iterate over 6400 items in a list and tick each individual item which could cause a tick to last 2 seconds so for every tick which is meant to be 1/20th of a second it would take 200/20th of a second to tick which would be bad for big servers.

 

I then came up with threading itemdrops.

 

when an item needs to drop it will be added to a blockingqueue to stop conncurent thread editing of variables, nasty stuff.

 

so then in the new thread that stops itself when it has nothing more to tick until its re started later by another player wanting to drop items

 

the main minecraft thread will add to the blocking queue and ask that the dropping thread pause until adding to the blockqueue has finished so that the main thread doesn't hang while waiting to put items into the queue

 

the second thread runs every 1/20th of a second obviously this thread doesn't take into account the main thread so it could be running faster than the minecraft thread or slower.

 

this second thread will peak and then grab an itemstacks to tick and when the item needs is to be ticked next tick it will add it to a different blocking queue that the main thread picks up and then drops the item on the next tick (have to pass it back to another blocking queue because I cannot drop the item directly from the other thread because of concurrency issues.

 

so a breakdown:

a) depending on how many items need to drop it will increase the amount of ticks between item drops minimum 10 ticks spacing between each item drop.

b) tries to make the biggest itemstack for that player as to lower entity counts.

c) ticks itemTick countdown on another thread so that the mainthread can continue to run without sticking on the iteration

d) stops thread when not needed

 

What I wish to know is whether or not there is a better solution to this that would cause the least amount of lag as possible on the main thread and maybe even decrease lag on the secondary thread.

 

the example I use is extreme, there are config settings so the user can set it to whatever they want if they want to set it ridiculously high as these examples show then that is their stupidity for allowing it but I would still be welcome to finding a cheaper solution

 

big wall of code over now for the big wall of code :D

 

UtilsItem class

 

//Main.cc refrences my config file
public class UtilsItem
{

private EntityPlayer	player;
private ItemStack[]		items			= new ItemStack[Main.cc.maxItemStack * 64];
private int				currentStack	= 0;
private ItemStack[]		newItems		= new ItemStack[Main.cc.maxItemStack];
private int				amountOfItems	= 0;
private int				ticksCallback	= 1;
private int				increaseTicksBy = 5;
private int				itemID			= 0;
private int				itemMeta		= 0;

private World w;
private int x, y, z;

public void add(EntityPlayer p, ItemStack item)
{
	player = p;
	items[currentStack] = item;
	currentStack++;
}

public void addToDropList()
{
	for (int i = 0; i < items.length; i++)
	{
		if(items[i] != null) this.amountOfItems += items[i].stackSize;
		else break;
	}
	itemID = items[0].itemID;
	itemMeta = items[0].getItemDamage();

	if(this.amountOfItems >= 1792)
	{
		Utils.log("Player : " + player.username + " May be trying to grief the server with mass item drops");
		Utils.log("He is trying to drop " + this.amountOfItems);
		Utils.log("To reduce the amount of items droped we will increase the amount of time it takes for all the items to drop");
		Utils.log("and make all the stacks drop a maxinum of 64, rather than stacks of 1 items");
		Utils.log("His coordinates are X " + player.posX + ", Y "+ player.posY + ", Z " + player.posZ);
		increaseTicksBy = 10;
	}
	for (int i = 0; i < newItems.length; i++)
	{
		if (this.amountOfItems >= 64)
		{
			newItems[i] = new ItemStack(itemID, 64, itemMeta);
			Utils.dropItem(player, newItems[i], true, ticksCallback);
			ticksCallback += increaseTicksBy;
			this.amountOfItems -= 60;
		}
		else
		{
			if (this.amountOfItems > 0)
			{
				newItems[i] = new ItemStack(itemID, this.amountOfItems, itemMeta);
				Utils.dropItem(player, newItems[i], true, ticksCallback);
				ticksCallback += increaseTicksBy;
				this.amountOfItems = 0;
			}
			else break;
		}
	}
}
}

 

 

when the above object has been finished with its sent to Utils.dropItem()

 

public static void dropItem(EntityPlayer p,
		ItemStack itemstack, boolean doTickCallBack, int maxTicks)
{		if (itemstack.itemID != 0)
	{
			if (Minecraft.getMinecraft().isSingleplayer()
					&& !Minecraft.getMinecraft().getIntegratedServer().getPublic()
					&& !Minecraft.getMinecraft().getIntegratedServer().isDedicatedServer())
			{
				if (doTickCallBack == true) ServerTickHandler
						.addDropItemQueue(new DropItemQueue(maxTicks, p, itemstack));
				else p.entityDropItem(itemstack, 1.0F);
			}
		}
}

 

 

specifically if (doTickCallBack == true) ServerTickHandler

.addDropItemQueue(new DropItemQueue(maxTicks, p, itemstack));

 

will call in my serverTickHandler

 

public static BlockingQueue<DropItemQueue>		dropItemQueue			= new LinkedBlockingQueue<DropItemQueue>();

public static BlockingQueue<StrikeThunderQueue>	dropThunderNow			= new LinkedBlockingQueue<StrikeThunderQueue>();
public static BlockingQueue<DropItemQueue>		dropItemNow				= new LinkedBlockingQueue<DropItemQueue>();

/**
 * <pre>Drop item from player in X ticks (Specified in @Param element)
 * Causes Thread to pause for 1 millisecond if unsuccessful in adding to queue.</pre>
 * @param element contains a object of DropItemQueue
 * @exception InterruptedException
 */
public static void addDropItemQueue(DropItemQueue element)
{
	while (dropItemQueue.offer(element) == false)
	{
		try
		{
			Thread.sleep(1);
		}
		catch (InterruptedException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
/**
 * <pre>Drops item from player in 1 ticks (Specified in @Param element)
 * Causes Thread to pause for 1 millisecond if unsuccessful in adding to queue.</pre>
 * @param element contains a object of DropItemQueue
 * @exception InterruptedException
 */
public static void addToDropItemNow(DropItemQueue element)
{
	while (dropItemNow.offer(element) == false)
	{
		try
		{
			Thread.sleep(1);
		}
		catch (InterruptedException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
/**
 * Tries get a DropItemQueue element from dropItemQueue.
 * @return DropItemQueue or null if no elements are found.
 */
public static DropItemQueue getDropItemQueue()
{
	return dropItemQueue.poll();
}
/**
 * Tries get a DropItemQueue element from dropItemNow.
 * @return DropItemQueue or null if no elements are found.
 */
public static DropItemQueue getDropItemQueueNow()
{
	return dropItemNow.poll();
}
/**
 * Checks to see if there is an element in dropItemQueue without removing it.
 * @return DropItemQueue or null
 */
public static DropItemQueue peakDropItemQueue()
{
	return dropItemQueue.peek();
}
/**
 * <pre>When a tick in the game has happened.
 * Starts and stops running threads when there is nothing to do.
 * </pre>
 */
private void onTickInGame()
{
	if (peakDropItemQueue() != null) Main.dih.resumeThread();

	DropItemQueue diqn = getDropItemQueueNow();
	if(diqn != null)
	if(diqn.finalTick() != true) addToDropItemNow(diqn);
}

 

 

DropItemHandler class

 

package jordan30001.testMod.common.core.handlers.iterators;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Iterator;

import jordan30001.testMod.common.core.handlers.ServerTickHandler;
import jordan30001.testMod.common.core.handlers.queues.DropItemQueue;
import jordan30001.testMod.utils.Utils;
import jordan30001.testMod.utils.WorldData;

public class DropItemHandler implements Runnable
{
private Thread	runner;

/**
 * Creates a new thread for dealing with Item drops when using certain
 * tools.
 * 
 * @param threadName Name of the thread.
 */
public DropItemHandler(String threadName)
{
	runner = new Thread(this, threadName);
}

/**
 * The thread that is spawned when this object is created.
 */
public void run()
{
	// long start = System.currentTimeMillis();
	synchronized (runner)
	{
		while (true)
		{
			while (ServerTickHandler.peakDropItemQueue() == null)
				try
				{
					runner.wait();
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			DropItemQueue dropItemQueue = ServerTickHandler.getDropItemQueue();
			if (dropItemQueue != null)
			{
				if (!dropItemQueue.tick())
				{
					ServerTickHandler.addDropItemQueue(dropItemQueue);
				}
				else
				{
					ServerTickHandler.addToDropItemNow(dropItemQueue);
				}
			}
		}
	}


	// long end = System.currentTimeMillis();

	// NumberFormat formatter = new DecimalFormat("#0.00000");

}

/**
 * Starts the thread.
 * 
 * @throws IllegalStateException
 */
public void startThread()
{
	try
	{
		runner.start();
	}
	catch (IllegalStateException e)
	{
		e.printStackTrace();
	}
}
}

 

Link to comment
Share on other sites

just a question but why not just give them the items instead of dropping them

 

because math

 

player invent = 28

if dropping 100 stacks that's 72 stacks either lost because no room OR I still drop the 72 stacks.

 

not a solution but thanks for taking the time to read this.

 

Link to comment
Share on other sites

Dropping 100 stacks per player doesn't seem like a good idea to me. Not only from a performance point of view (even if you spawn them delayed, ticking that many Items can easily kill a server), but also from a usability POV. What should a player do with these 100 stacks? manually insert them into a chest? Wait for them to despawn? seriously, I think you need to review your idea :D

 

By ticking the items do you mean ticking them from the main thread or the secondary thread?

 

I chose 100 stacks as a over shoot because I have to take into account the "stupid" people that take advantage of the config settings.

 

by default its only around 5-6 stacks but if the end user really wanted to they could they could change it to drop a trillion stacks (if they have enough ram)

 

From an end user point of view that's totally up to them what they change their settings to.

 

Why I created this thread was to make it the most efficient on resources as possible.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.