Index: net/sf/l2j/gameserver/GameTimeController.java =================================================================== --- net/sf/l2j/gameserver/GameTimeController.java (revision 1283) +++ net/sf/l2j/gameserver/GameTimeController.java (working copy) @@ -20,7 +20,6 @@ import java.text.SimpleDateFormat; import java.util.Date; -import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.logging.Logger; @@ -28,6 +27,7 @@ import net.sf.l2j.gameserver.ai.CtrlEvent; import net.sf.l2j.gameserver.instancemanager.DayNightSpawnManager; import net.sf.l2j.gameserver.model.L2Character; +import java.util.concurrent.locks.*; /** * This class ... @@ -47,11 +47,15 @@ protected static long _gameStartTime; protected static boolean _isNight = false; - private static List _movingObjects = new FastList(); + private static FastList _movingObjects = new FastList(); protected static TimerThread _timer; + protected static MoveObjectsThread _mover; private ScheduledFuture _timerWatcher; + public final Lock lock = new ReentrantLock(); + public final Condition tickCondition = lock.newCondition(); + /** * one ingame day is 240 real minutes */ @@ -68,6 +72,9 @@ _timer = new TimerThread(); _timer.start(); + _mover = new MoveObjectsThread(_movingObjects); + _mover.start(); + _timerWatcher = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new TimerWatcher(), 0, 1000); ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new BroadcastSunState(), 0, 600000); @@ -97,10 +104,12 @@ * @param cha The L2Character to add to movingObjects of GameTimeController * */ - public synchronized void registerMovingObject(L2Character cha) + public void registerMovingObject(L2Character cha) { if(cha == null) return; - if (!_movingObjects.contains(cha)) _movingObjects.add(cha); + synchronized(_movingObjects) { + if (!_movingObjects.contains(cha)) _movingObjects.add(cha); + } } /** @@ -115,45 +124,49 @@ *
  • Create a task to update the _knownObject and _knowPlayers of each L2Character that finished its movement and of their already known L2Object then notify AI with EVT_ARRIVED


  • * */ - protected synchronized void moveObjects() - { - // Get all L2Character from the ArrayList movingObjects and put them into a table - L2Character[] chars = _movingObjects.toArray(new L2Character[_movingObjects.size()]); - - // Create an ArrayList to contain all L2Character that are arrived to destination - List ended = null; - - // Go throw the table containing L2Character in movement - for (int i = 0; i < chars.length; i++) + + class MoveObjectsThread extends Thread { + private final FastList chars; + private int oldticks; + public MoveObjectsThread(FastList _chars) { - - L2Character cha = chars[i]; - - // Update the position of the L2Character and return True if the movement is finished - boolean end = cha.updatePosition(_gameTicks); - - // If movement is finished, the L2Character is removed from movingObjects and added to the ArrayList ended - if (end) - { - _movingObjects.remove(cha); - if (ended == null) ended = new FastList(); - - ended.add(cha); + chars = _chars; + } + public void run() + { + L2Character cha; + for (;;) { + + lock.lock(); + try + { + while (oldticks == _gameTicks || chars.isEmpty()) + { + if (oldticks != _gameTicks) oldticks = _gameTicks; + tickCondition.await(); + } + } catch (InterruptedException e) { return; } + finally {lock.unlock();} + + for (FastList.Node e = chars.head(), end = chars.tail(); e!=end; e = e.getNext()) + { + cha = e.getValue(); + if (cha.updatePosition(_gameTicks)) + { + synchronized (chars) { chars.remove(cha); } + ThreadPoolManager.getInstance().executeTask(new MovingObjectArrived(cha)); + } + } + oldticks = _gameTicks; } } - - // Create a task to update the _knownObject and _knowPlayers of each L2Character that finished its movement and of their already known L2Object - // then notify AI with EVT_ARRIVED - // TODO: maybe a general TP is needed for that kinda stuff (all knownlist updates should be done in a TP anyway). - if (ended != null) - ThreadPoolManager.getInstance().executeTask(new MovingObjectArrived(ended)); - } - + public void stopTimer() { _timerWatcher.cancel(true); _timer.interrupt(); + _mover.interrupt(); } class TimerThread extends Thread @@ -175,29 +188,26 @@ { for (;;) { + lock.lock(); int _oldTicks=_gameTicks; // save old ticks value to avoid moving objects 2x in same tick long runtime = System.currentTimeMillis() - _gameStartTime; // from server boot to now _gameTicks = (int) (runtime / MILLIS_IN_TICK); // new ticks value (ticks now) - if (_oldTicks != _gameTicks) moveObjects(); // XXX: if this makes objects go slower, remove it - // but I think it can't make that effect. is it better to call moveObjects() twice in same - // tick to make-up for missed tick ? or is it better to ignore missed tick ? - // (will happen very rarely but it will happen ... on garbage collection definitely) + if (_oldTicks != _gameTicks) tickCondition.signalAll(); runtime = (System.currentTimeMillis() - _gameStartTime) - runtime; // calculate sleep time... time needed to next tick minus time it takes to call moveObjects() int sleepTime = 1 + MILLIS_IN_TICK - ((int) runtime) % MILLIS_IN_TICK; - //_log.finest("TICK: "+_gameTicks); - - sleep(sleepTime); // hope other threads will have much more cpu time available now - // SelectorThread most of all + lock.unlock(); + sleep(sleepTime); } } catch (Exception e) { + lock.unlock(); _error = e; } } @@ -224,20 +234,18 @@ */ class MovingObjectArrived implements Runnable { - private final List _ended; + private final L2Character _ended; - MovingObjectArrived(List ended) + MovingObjectArrived(L2Character ended) { _ended = ended; } public void run() { - for (L2Character cha : _ended) - { - cha.getKnownList().updateKnownObjects(); - cha.getAI().notifyEvent(CtrlEvent.EVT_ARRIVED); - } + _ended.getKnownList().updateKnownObjects(); + if (_ended.hasAI()) + _ended.getAI().notifyEvent(CtrlEvent.EVT_ARRIVED); } }