Jump to content
  • 0

Respawn After...(Threadpoolmanager)


Question

Posted (edited)

Hello guys,
I have a custom zone and whenever you die in that zone it shows you the timer when you will respawn and after timer reaches 0 it respawns you.This was my first try:
 

	public void onDieInside(L2Character character)
	{
		
		if(character instanceof L2PcInstance){
			final L2PcInstance player = (L2PcInstance) character;
			countdown = 5;
			ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new Runnable(){
				@Override
				public void run(){
						if(player.isDead()){
							if(countdown >0){
							SystemMessage smg = SystemMessage.getSystemMessage(SystemMessageId.RESPAWN_AFTER_S1_SECONDS);
							smg.addString(String.valueOf(countdown));
							player.sendPacket(SystemMessageId.RESPAWN_AFTER_S1_SECONDS);
							countdown--;
							}else if(countdown == 0){
				                player.doRevive();
				    			int x =	174132 + (int)(Math.random() * ((179862 - 174132) + 1));
				    			int y = 112225 + (int)(Math.random() * ((117715 - 112225) + 1));
				    			int z = -7708;
				    			Location loc = new Location(x, y, z);
				                player.teleToLocation(loc,0);
							}
						}
				}
				
			}
			,0,1000);
			
		}
		
	}

It works fine for first time,but then it just get shorter and shorter.I believe it's because thread doesn't stop working after respawning player,so whenever a player dies again the countdown is set to 5,but there is 2threads running and subtracting 1from countdown and then threads just keep increasing.
My second try was this method:

	public void onDieInside(L2Character character)
	{
		
		if(character instanceof L2PcInstance){
			final L2PcInstance player = (L2PcInstance) character;
			countdown = 5;
			while(countdown > 0 && player.isDead()){
				ThreadPoolManager.getInstance().scheduleGeneral(new showCountdown(countdown,player), 6000-countdown*1000);
				countdown--;
			}
			ThreadPoolManager.getInstance().scheduleGeneral(new Runnable(){
				@Override
				public void run(){
					if(player.isDead()){
	                player.doRevive();
	    			int x =	174132 + (int)(Math.random() * ((179862 - 174132) + 1));
	    			int y = 112225 + (int)(Math.random() * ((117715 - 112225) + 1));
	    			int z = -7708;
	    			Location loc = new Location(x, y, z);
	                player.teleToLocation(loc,0);
					}
				}
			}, 6000);
			
		
			
		}
		
	}

This is showCountdown class:

	class showCountdown implements Runnable{
		int timer = 0;
		L2PcInstance player;
		public showCountdown(int countdown,L2PcInstance p){
			timer = countdown;
			player = p;
		}
		@Override
		public void run(){
			SystemMessage smg = SystemMessage.getSystemMessage(SystemMessageId.RESPAWN_AFTER_S1_SECONDS);
			smg.addNumber(timer);
			player.sendPacket(smg);
		}
	}

It works just fine,but it seems dumb just to create so many Threads for a simple countdown.

Edited by StealthyS4m

15 answers to this question

Recommended Posts

  • 0
Posted

The second code is about fine, you could have done it with one thread but thats nothing for the resources.

 

Never use that shutdown, it will destroy your thread manager.

You can cancel thread by first saving it as ScheduledFuture<?>, for example:

ScheduledFuture<?> thread = ThreadPoolManager.getInstance().schedule(new Something(), 5000L);
thread.cancel(false);

However, in the first program it would be hard to use that method(you would need to get ScheduledFuture<?> thread from inside somehow)

  • 0
Posted

If you can read the whole post,I've got 2ways to do my thing and one of them is working,just I am not sure if it's the best way.
I may have been a bit unclear in my first post.But countdown--; is the number that will be written on sysmsg.
For example,at first countdown is 5,after second the thread runs again and this time countdown is 4,so it will say "You will respawn after 4 seconds",then "You will respawn after 3seconds" and so on.The reason it gets shorter is because when you die second time my method creates another thread,so now there is two threads,which are subtracting countdown.The more you die,the less respawn time is.
To fix it I need to know how to destroy ThreadPoolManager after it runs everything it needs:
 

				public void run(){
						if(player.isDead()){
							if(countdown >0){
							SystemMessage smg = SystemMessage.getSystemMessage(SystemMessageId.RESPAWN_AFTER_S1_SECONDS);
							smg.addString(String.valueOf(countdown));
							player.sendPacket(SystemMessageId.RESPAWN_AFTER_S1_SECONDS);
							countdown--;
							}else if(countdown == 0){
				                player.doRevive();
				    			int x =	174132 + (int)(Math.random() * ((179862 - 174132) + 1));
				    			int y = 112225 + (int)(Math.random() * ((117715 - 112225) + 1));
				    			int z = -7708;
				    			Location loc = new Location(x, y, z);
				                player.teleToLocation(loc,0);
							}else{ 
                                                      DestroyThisThreadSomehow();
}
						}
				}



P.S. I believe I have problems expressing my problems associated with programming in English language,cause I am not very good in both of them. ^_^

  • 0
Posted

You can use my code that i have wrote for starting new Event:

	private static class StartEventThread implements Runnable
	{
		private static final int[] STOP_TIMES = {1800, 900, 600, 300, 60, 30, 15, 10, 5, 0};//In which seconds Thread have to stop to show the message
		private final AbstractFightClub duplicatedEvent;//This is for starting event, you don't need it
		private final int secondsToStartEvent;

		private StartEventThread(AbstractFightClub duplicatedEvent, int secondsToStartEvent)
		{
			this.duplicatedEvent = duplicatedEvent;
			this.secondsToStartEvent = secondsToStartEvent;
		}

		@Override
		public void run()
		{
			try
			{
				if(getInstance().isNextEventBlocked())
					return;

				int timeToShow = -1;//Time in Seconds to show in the message
				int timeToWait = -1;//Seconds to wait until next STOP_TIME
                                //This is a bit advanced, the reason is if i would start StartEventThread to finish after 1900 seconds, it would first wait 100 seconds, print the message and then go on normally
				for(int i = 0;i < STOP_TIMES.length;i++)
				{
					if (secondsToStartEvent >= STOP_TIMES[i])
					{
						if (secondsToStartEvent == STOP_TIMES[i])
						{
							timeToShow = STOP_TIMES[i];
							if(i == STOP_TIMES.length - 1)
								timeToWait = 0;
							else
								timeToWait = STOP_TIMES[i] - STOP_TIMES[i + 1];
						}
						else
						{
							timeToWait = secondsToStartEvent - STOP_TIMES[i];
						}
						break;
					}
				}

				if(timeToShow >= 0)//Also timeToShow is only above 0, if left seconds until event == one of the STOP_TIMES. So in the example with 1900 seconds, message wouldnt show
				{
					if(timeToShow > 60)
						sendMsg(duplicatedEvent.getName() + " will start in " + (timeToShow / 60) + " minutes!");
					else if(timeToShow == 60)
						sendMsg(duplicatedEvent.getName() + " will start in 1 minute!");
					else if(timeToShow > 0)
						sendMsg(duplicatedEvent.getName() + " will start in " + timeToShow + " seconds!");
					else if(timeToShow == 0)
						sendMsg(duplicatedEvent.getName() + " Started!");
				}

				if(timeToWait > 0)
				{
					ThreadPoolManager.getInstance().schedule(new StartEventThread(duplicatedEvent, secondsToStartEvent - timeToWait), (long) timeToWait * 1000L);
				}
				else
				{
					getInstance().startCurrentEvent();
				}
			}
			catch (RuntimeException e)
			{
				LOG.error("Error while launching Event ", e);
			}
		}
	}
}
  • 0
Posted

About the problem of shorter waiting if you die once again. There are 2 ways:

1. When the thread stops to send the message, you can check if player is still dead, if not then cancel the thread

2. You can save thread somewhere and when player have been resurrected you can cancel the thread

  • 0
Posted (edited)

You mean something like this:
 

ThreadPoolManager tpm = ThreadPoolManager.getInstance();
if(condition_means_shutdown)
tpm.shutdown();

Is my method,the one who works(second one) fine?Or does it use too many resources for such an easy task?

Edited by StealthyS4m
  • 0
Posted (edited)
ScheduledFuture<?> thread = ThreadPoolManager.getInstance().schedule(new Something(), 5000L);
class Something implements Runnable{
    public void run(){
if(conditionMeansCancel){
cancel(thread);
}
}
}
public void cancel(ScheduledFuture<?> thread){
thread.cancel();
}

Wouldn't it make it work?I believe only in C++ it creates new copy when passing variable to argument.

Edited by StealthyS4m
  • 0
Posted

You could make something like that:

private ScheduledFuture<?> thread;

public void startThread()
{
	thread = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new Something(), 5000L, 5000L);
}

private static class Something implements Runnable
{
	@Override
	public void run()
	{
		getInstance().stopThread();
	}
}

private void stopThread()
{
	thread.cancel(false);
}

private static class SingletonHolder
{
	private static final MainClass instance = new MainClass();
}

public static MainClass getInstance()
{
	return SingletonHolder.instance;
}

But it's a lot better to use schedule(and if you want to cancel it, just put if(shouldThreadBeCancelled()) return;) than scheduleGeneralAtFixedRate and cancelling it that kind of way.

  • 0
Posted


+

+ @Override

+ public void onDieInside(final L2Character character)

+ {

+ if (character instanceof L2PcInstance)

+ {

+ final L2PcInstance activeChar = ((L2PcInstance) character);

+ if(revive)

+ {

+ ThreadPoolManager.getInstance().scheduleGeneral(new Runnable()

+ {

+ @Override

+ public void run()

+ {

+ activeChar.doRevive();

+ heal(activeChar);

+ int[] loc = spawn_loc[Rnd.get(spawn_loc.length)];

+ activeChar.teleToLocation(loc[0]+Rnd.get(-radius,radius), loc[1]+Rnd.get(-radius,radius), loc[2]);

+ }

+ },revive_delay*1000);

+ }

+ }

+ }

+

Vampir thread really doesnt stops also here?

  • 0
Posted

It does, but we are talking about first example(with scheduleGeneralAtFixedRate(Runnable, long, long) method)

  • 0
Posted

Yes, but your code isn't complete.

The goal:

-6 Seconds: Send "Respawn in 6 Seconds" message
-5 Seconds: Send "Respawn in 5 Seconds" message
-4 Seconds: Send "Respawn in 4 Seconds" message
-3 Seconds: Send "Respawn in 3 Seconds" message
-2 Seconds: Send "Respawn in 2 Seconds" message
-1 Seconds: Send "Respawn in 1 Second" message
0 Second: Respawn Player

The First code is bad because of scheduleGeneralAtFixedRate. This method is good if you want to make same repeated task every x amount of time, cancel it in rare cases. It's hard to cancel the method and to get "countdown" variable from inside the method.

The Second Code is quite fine, but it would be better with just 1 Thread.

Your code doesn't print any messages

  • 0
Posted (edited)

You got the choice to make 5 tasks firing one event, or one task firing 5 different events.

 

I had to code a similar thing for MDT rework on aCis. It looks like vampir code.

protected MonsterRace()
{
	...
            ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new Announcement(), 0, 1000);
    }

	private class Announcement implements Runnable
	{
		public Announcement()
		{
		}
		
		@Override
		public void run()
		{
			if (_finalCountdown > 1200)
				_finalCountdown = 0;
			
			switch (_finalCountdown)
			{
				case 0:
					newRace();
					newSpeeds();
					
					_state = RaceState.ACCEPTING_BETS;
					_packet = new MonRaceInfo(_codes[0][0], _codes[0][1], getMonsters(), getSpeeds());
					
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, _packet, SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_TICKETS_AVAILABLE_FOR_S1_RACE).addNumber(_raceNumber));
					break;
				
				case 30: // 30 sec
				case 60: // 1 min
				case 90: // 1 min 30 sec
				case 120: // 2 min
				case 150: // 2 min 30
				case 180: // 3 min
				case 210: // 3 min 30
				case 240: // 4 min
				case 270: // 4 min 30 sec
				case 330: // 5 min 30 sec
				case 360: // 6 min
				case 390: // 6 min 30 sec
				case 420: // 7 min
				case 450: // 7 min 30
				case 480: // 8 min
				case 510: // 8 min 30
				case 540: // 9 min
				case 570: // 9 min 30 sec
				case 630: // 10 min 30 sec
				case 660: // 11 min
				case 690: // 11 min 30 sec
				case 720: // 12 min
				case 750: // 12 min 30
				case 780: // 13 min
				case 810: // 13 min 30
				case 870: // 14 min 30 sec
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_TICKETS_NOW_AVAILABLE_FOR_S1_RACE).addNumber(_raceNumber));
					break;
				
				case 300: // 5 min
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_TICKETS_NOW_AVAILABLE_FOR_S1_RACE).addNumber(_raceNumber), SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_TICKETS_STOP_IN_S1_MINUTES).addNumber(10));
					break;
				
				case 600: // 10 min
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_TICKETS_NOW_AVAILABLE_FOR_S1_RACE).addNumber(_raceNumber), SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_TICKETS_STOP_IN_S1_MINUTES).addNumber(5));
					break;
				
				case 840: // 14 min
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_TICKETS_NOW_AVAILABLE_FOR_S1_RACE).addNumber(_raceNumber), SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_TICKETS_STOP_IN_S1_MINUTES).addNumber(1));
					break;
				
				case 900: // 15 min
					_state = RaceState.WAITING;
					
					calculateOdds();
					
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_TICKETS_NOW_AVAILABLE_FOR_S1_RACE).addNumber(_raceNumber), SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_S1_TICKET_SALES_CLOSED));
					break;
				
				case 960: // 16 min
				case 1020: // 17 min
					final int minutes = (_finalCountdown == 960) ? 2 : 1;
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_S2_BEGINS_IN_S1_MINUTES).addNumber(minutes));
					break;
				
				case 1050: // 17 min 30 sec
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_S1_BEGINS_IN_30_SECONDS));
					break;
				
				case 1070: // 17 min 50 sec
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_S1_COUNTDOWN_IN_FIVE_SECONDS));
					break;
				
				case 1075: // 17 min 55 sec
				case 1076: // 17 min 56 sec
				case 1077: // 17 min 57 sec
				case 1078: // 17 min 58 sec
				case 1079: // 17 min 59 sec
					final int seconds = 1080 - _finalCountdown;
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_BEGINS_IN_S1_SECONDS).addNumber(seconds));
					break;
				
				case 1080: // 18 min
					_state = RaceState.STARTING_RACE;
					_packet = new MonRaceInfo(_codes[1][0], _codes[1][1], getMonsters(), getSpeeds());
					
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_RACE_START), _sound1, _sound2, _packet);
					break;
				
				case 1085: // 18 min 5 sec
					_packet = new MonRaceInfo(_codes[2][0], _codes[2][1], getMonsters(), getSpeeds());
					
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, _packet);
					break;
				
				case 1115: // 18 min 35 sec
					_state = RaceState.RACE_END;
					
					// Populate history info with data, stores it in database.
					final HistoryInfo info = _history.get(_history.size() - 1);
					info.setFirst(getFirstPlace());
					info.setSecond(getSecondPlace());
					info.setOddRate(_odds.get(getFirstPlace() - 1));
					
					saveHistory(info);
					clearBets();
					
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_FIRST_PLACE_S1_SECOND_S2).addNumber(getFirstPlace()).addNumber(getSecondPlace()), SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_S1_RACE_END).addNumber(_raceNumber));
					_raceNumber++;
					break;
				
				case 1140: // 19 min
					Broadcast.toAllPlayersInZoneType(L2DerbyTrackZone.class, new DeleteObject(getMonsters()[0]), new DeleteObject(getMonsters()[1]), new DeleteObject(getMonsters()[2]), new DeleteObject(getMonsters()[3]), new DeleteObject(getMonsters()[4]), new DeleteObject(getMonsters()[5]), new DeleteObject(getMonsters()[6]), new DeleteObject(getMonsters()[7]));
					break;
			}
			_finalCountdown += 1;
		}
	}

It's a 20 minutes cycle event, you obviously have to recode, like inverting the timer to reuse the current timer as variable, add a stopping stuff, etc.

Edited by Tryskell
Guest
This topic is now closed to further replies.


  • Posts

    • Thank you for your feedback. To help me improve the system, please leave your comments or report any bugs. I will try to release weekly updates. 
    • Thank you for your feedback, SkyLord — even the negative kind.   Let me give you some context on who we actually are, since you asked. L2-Scripts has been around for nearly 20 years. Over that time, we've worked with a large number of developers — I've personally seen probably half of every serious (and not-so-serious) developer in this scene. The most significant figure in our studio's history was Bonux. Together with a small team of assistants, he was essentially the backbone of our old codebase, and we were the ones who brought virtually every chronicle to the community — Essence, Classic, Main — all of it came through us.   Back then, we worked at scale. That era is over. Today, L2-Scripts is no longer a studio in the traditional sense. It's more of a developer collective. I work alongside the best developers I've encountered in 20 years. Bonux is still with us on Classic. A new developer named Den has joined — in just 3 years he became one of the top Essence developers in the world. Every one of us works independently, but we're united by long-standing friendships and shared projects.   We no longer chase every chronicle or every client. For the past several years, we've been focused exclusively on specific long-term projects where we're paid a proper salary — and when you focus like that, with 20 years of experience behind you, the quality of what you produce is on a completely different level. Our code is currently running on nearly all top Essence and Classic servers. We're now bringing Main (Last Protocol) up to that same standard.   You mentioned Nexvil. Yes, he worked with us. That was a difficult period — the lead developer at the time was not up to the standard we hold ourselves to. It was a short period, and it's long behind us.   As for the prices — I'd ask you not to judge a product by whether you personally can afford it. That's not a fair measure of value. The people running top-tier servers understand what they're paying for. If our pricing doesn't fit your budget, that's completely fine — but downvoting a service simply because it feels expensive to you doesn't reflect well, and it certainly doesn't make the product worse.   A special thank you to eMommy and Zenith for the honest and fair words — it genuinely means a lot. It's good to know there are people in this community who judge things as they are.
    • Just because someone does professional sales doesn’t mean everything is overpriced. If the products he provides are stable and good quality, then I see nothing wrong with it, and those prices aren’t even bad. Remember, we’re living in 2026, not 10 years in the past. Your mindset is probably stuck on buying the cheapest products because maybe you can’t afford better ones. And of course, there’s always someone who likes blaming others. I think you should mind your own business instead of blaming someone for how they run theirs. If you really think his prices are too expensive, then do better than him.
    • I have to be fair here and state that almost all 'good' servers on Essence/Classic are using bonux/l2scripts files and 10k for a decent essence build imo is not that bad of a price as long as nexvil is not touching these files
  • Topics

×
×
  • Create New...

Important Information

This community uses essential cookies to function properly. Non-essential cookies and third-party services are used only with your consent. Read our Privacy Policy and We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue..