Use delayed event queue to improve performance of game startup and event processing in general

This commit is contained in:
drdev
2014-10-01 20:29:01 +00:00
parent 1f277a1e6b
commit c5ac3c652e
3 changed files with 49 additions and 35 deletions

View File

@@ -5,6 +5,8 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.lang3.tuple.Pair;
@@ -57,6 +59,7 @@ import forge.model.FModel;
import forge.player.GamePlayerUtil;
import forge.properties.ForgePreferences.FPref;
import forge.util.Lang;
import forge.util.ThreadUtil;
import forge.util.gui.SGuiChoose;
import forge.util.maps.MapOfLists;
import forge.view.CardView;
@@ -64,17 +67,50 @@ import forge.view.LocalGameView;
import forge.view.PlayerView;
public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
private static FControlGameEventHandler mainHandler;
public static void processEventsAfterInput() {
if (mainHandler.processEventsSchedule != null) {
mainHandler.processEventsSchedule.cancel(false); //cancel any currently scheduled processing
}
mainHandler.processEventsDelay = 10; //speed up event processing until an event has been raised
mainHandler.processEventsSchedule = ThreadUtil.delay(mainHandler.processEventsDelay, mainHandler.processEvents);
}
private final LocalGameView gameView;
private final boolean isMainHandler;
private final ConcurrentLinkedQueue<GameEvent> eventQueue = new ConcurrentLinkedQueue<GameEvent>();
public FControlGameEventHandler(final LocalGameView gameView0, final boolean isMainHandler0) {
gameView = gameView0;
isMainHandler = isMainHandler0;
if (isMainHandler) {
mainHandler = this;
}
processEvents.run(); //start event processing loop
}
private ScheduledFuture<?> processEventsSchedule;
private int processEventsDelay = 100;
private final Runnable processEvents = new Runnable() {
@Override
public void run() {
if (!eventQueue.isEmpty()) {
processEventsDelay = 100; //reset to 100 once an event has been raised
gameView.updateViews();
GameEvent ev;
while ((ev = eventQueue.poll()) != null) {
ev.visit(FControlGameEventHandler.this);
}
}
processEventsSchedule = ThreadUtil.delay(processEventsDelay, this); //only process events 10 times per second by default to improve performance
}
};
@Subscribe
public void receiveGameEvent(final GameEvent ev) {
ev.visit(this);
eventQueue.add(ev);
}
private final AtomicBoolean phaseUpdPlanned = new AtomicBoolean(false);
@@ -103,11 +139,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
}
private void updateCombat() {
if (!isMainHandler) { return; }
gameView.updateViews();
if (combatUpdPlanned.getAndSet(true)) { return; }
if (!isMainHandler || combatUpdPlanned.getAndSet(true)) { return; }
FThreads.invokeInEdtNowOrLater(gameView.getGui(), new Runnable() {
@Override
@@ -144,7 +176,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(GameEventAnteCardsSelected ev) {
if (!isMainHandler) { return null; }
gameView.updateViews();
final List<CardView> options = Lists.newArrayList();
for (final Entry<Player, Card> kv : ev.cards.entries()) {
final CardView fakeCard = new CardView(-1); //use fake card so real cards appear with proper formatting
@@ -162,7 +193,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
return null;
}
gameView.updateViews();
FThreads.invokeInEdtNowOrLater(gameView.getGui(), new Runnable() {
@Override
public void run() {
@@ -187,9 +217,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
@Override
public Void visit(GameEventGameFinished ev) {
if (isMainHandler) {
gameView.updateViews();
}
FThreads.invokeInEdtNowOrLater(gameView.getGui(), new Runnable() {
@Override
public void run() {
@@ -200,6 +227,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
if (isMainHandler) {
MatchUtil.getController().finishGame();
gameView.updateAchievements();
mainHandler = null; //this can be reset after game is finished
}
}
});
@@ -219,7 +247,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(GameEventSpellAbilityCast event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
if (!stackUpdPlanned.getAndSet(true)) {
FThreads.invokeInEdtNowOrLater(gameView.getGui(), updStack);
}
@@ -229,7 +256,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(GameEventSpellResolved event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
if (!stackUpdPlanned.getAndSet(true)) {
FThreads.invokeInEdtNowOrLater(gameView.getGui(), updStack);
}
@@ -239,7 +265,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(GameEventSpellRemovedFromStack event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
if (!stackUpdPlanned.getAndSet(true)) {
FThreads.invokeInEdtNowOrLater(gameView.getGui(), updStack);
}
@@ -263,7 +288,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
if (event.player != null) {
// anything except stack will get here
gameView.updateViews();
updateZone(Pair.of(gameView.getPlayerView(event.player), event.zoneType));
}
return null;
@@ -273,7 +297,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(GameEventCardAttachment event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
final Game game = event.equipment.getGame();
final PlayerZone zEq = (PlayerZone)game.getZoneOf(event.equipment);
if (event.oldEntiy instanceof Card) {
@@ -318,7 +341,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(final GameEventCardTapped event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
return updateSingleCard(gameView.getCardView(event.card));
}
@@ -326,7 +348,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(final GameEventCardPhased event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
return updateSingleCard(gameView.getCardView(event.card));
}
@@ -334,7 +355,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(final GameEventCardDamaged event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
return updateSingleCard(gameView.getCardView(event.card));
}
@@ -342,7 +362,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(final GameEventCardCounters event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
return updateSingleCard(gameView.getCardView(event.card));
}
@@ -350,7 +369,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(final GameEventBlockersDeclared event) { // This is to draw icons on blockers declared by AI
if (!isMainHandler) { return null; }
gameView.updateViews();
for (MapOfLists<Card, Card> kv : event.blockers.values()) {
for (Collection<Card> blockers : kv.values()) {
updateManyCards(gameView.getCardViews(blockers));
@@ -368,8 +386,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
return null;
}
gameView.updateViews();
// Update all attackers.
// Although they might have been updated when they were tapped, there could be someone with vigilance, not redrawn yet.
updateManyCards(gameView.getCardViews(event.attackersMap.values()));
@@ -379,8 +395,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
@Override
public Void visit(GameEventCombatChanged event) {
if (!isMainHandler) { return null; }
gameView.refreshCombat();
updateCombat();
return null;
@@ -429,7 +443,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(GameEventCardChangeZone event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
if (event.from != null) {
updateZone(event.from);
}
@@ -446,7 +459,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(GameEventCardStatsChanged event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
final Iterable<CardView> cardViews = gameView.getCardViews(event.cards);
MatchUtil.getController().refreshCardDetails(cardViews);
return updateManyCards(cardViews);
@@ -456,7 +468,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(GameEventPlayerStatsChanged event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
for (final Player p : event.players) {
MatchUtil.getController().refreshCardDetails(gameView.getCardViews(p.getAllCards()));
}
@@ -467,7 +478,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(GameEventShuffle event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
updateZone(event.player.getZone(ZoneType.Library));
return null;
}
@@ -487,7 +497,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(GameEventManaPool event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
boolean invokeUpdate = false;
synchronized (manaPoolUpdate) {
if (!manaPoolUpdate.contains(event.player)) {
@@ -515,7 +524,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(GameEventPlayerLivesChanged event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
boolean invokeUpdate = false;
synchronized (livesUpdate) {
if (!livesUpdate.contains(event.player)) {
@@ -533,7 +541,6 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public Void visit(GameEventPlayerPoisoned event) {
if (!isMainHandler) { return null; }
gameView.updateViews();
boolean invokeUpdate = false;
synchronized (livesUpdate) {
if (!livesUpdate.contains(event.receiver)) {

View File

@@ -22,6 +22,7 @@ import java.util.Observer;
import java.util.concurrent.atomic.AtomicReference;
import forge.FThreads;
import forge.control.FControlGameEventHandler;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -66,7 +67,8 @@ public class InputProxy implements Observer {
*/
input.set(nextInput);
Runnable showMessage = new Runnable() {
@Override public void run() {
@Override
public void run() {
Input current = getInput();
gameView.getInputQueue().syncPoint();
//System.out.printf("\t%s > showMessage @ %s/%s during %s%n", FThreads.debugGetCurrThreadId(), nextInput.getClass().getSimpleName(), current.getClass().getSimpleName(), game.getPhaseHandler().debugPrintState());
@@ -84,6 +86,7 @@ public class InputProxy implements Observer {
public final void selectButtonOK() {
Input inp = getInput();
if (inp != null) {
FControlGameEventHandler.processEventsAfterInput();
inp.selectButtonOK();
}
}
@@ -96,6 +99,7 @@ public class InputProxy implements Observer {
public final void selectButtonCancel() {
Input inp = getInput();
if (inp != null) {
FControlGameEventHandler.processEventsAfterInput();
inp.selectButtonCancel();
}
}
@@ -105,6 +109,7 @@ public class InputProxy implements Observer {
if (inp != null) {
final Player player = gameView.getPlayer(playerView);
if (player != null) {
FControlGameEventHandler.processEventsAfterInput();
inp.selectPlayer(player, triggerEvent);
}
}
@@ -115,6 +120,7 @@ public class InputProxy implements Observer {
if (inp != null) {
final Card card = gameView.getCard(cardView);
if (card != null) {
FControlGameEventHandler.processEventsAfterInput();
return inp.selectCard(card, triggerEvent);
}
}
@@ -126,6 +132,7 @@ public class InputProxy implements Observer {
if (inp != null) {
final SpellAbility sa = gameView.getSpellAbility(ab);
if (sa != null) {
FControlGameEventHandler.processEventsAfterInput();
inp.selectAbility(sa);
}
}