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<L2Character> _movingObjects = new FastList<L2Character>();
+ private static FastList<L2Character> _movingObjects = new FastList<L2Character>();
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 @@
* <li>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 </li><BR><BR>
*
*/
- 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<L2Character> 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<L2Character> chars;
+ private int oldticks;
+ public MoveObjectsThread(FastList<L2Character> _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<L2Character>();
-
- 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<L2Character> 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<L2Character> _ended;
+ private final L2Character _ended;
- MovingObjectArrived(List<L2Character> 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);
}
}