From faec07b8d57d76bf7d692ace165fcf1239096d61 Mon Sep 17 00:00:00 2001 From: Rogiel Date: Fri, 27 May 2011 12:35:05 -0300 Subject: [PATCH] ThreadService implementation Signed-off-by: Rogiel --- .../ai/com/l2jserver/game/ai/MonsterAI.java | 38 +++++ .../ai/com/l2jserver/game/ai/NPCAI.java | 47 ++++++ .../l2jserver/db/dao/h2/H2CharacterDAO.java | 2 + .../db/dao/h2/H2CharacterFriendDAO.java | 2 + .../com/l2jserver/db/dao/h2/H2ClanDAO.java | 2 + .../com/l2jserver/db/dao/h2/H2ItemDAO.java | 2 + src/dao/com/l2jserver/db/dao/h2/H2NPCDAO.java | 2 + src/main/java/com/l2jserver/game/ai/AI.java | 33 +++- .../game/ai/desires/AttackDesire.java | 6 - .../com/l2jserver/game/ai/desires/Desire.java | 17 +- .../l2jserver/game/ai/desires/MoveDesire.java | 12 -- .../packet/client/CharacterActionPacket.java | 3 + .../packet/client/CharacterRequestBypass.java | 3 + .../java/com/l2jserver/model/world/NPC.java | 42 +++++ .../com/l2jserver/service/ServiceModule.java | 5 + .../service/cache/EhCacheService.java | 2 +- .../core/threading/ScheduledAsyncFuture.java | 29 ++++ .../service/core/threading/ThreadPool.java | 71 ++++++++ .../service/core/threading/ThreadService.java | 44 +++++ .../core/threading/ThreadServiceImpl.java | 151 ++++++++++++++---- .../service/game/npc/NPCService.java | 22 ++- .../service/game/npc/NPCServiceImpl.java | 57 ++++++- .../world/event/WorldEventDispatcherImpl.java | 25 +-- 23 files changed, 532 insertions(+), 85 deletions(-) create mode 100644 data/script/ai/com/l2jserver/game/ai/MonsterAI.java create mode 100644 data/script/ai/com/l2jserver/game/ai/NPCAI.java create mode 100644 src/main/java/com/l2jserver/service/core/threading/ScheduledAsyncFuture.java create mode 100644 src/main/java/com/l2jserver/service/core/threading/ThreadPool.java diff --git a/data/script/ai/com/l2jserver/game/ai/MonsterAI.java b/data/script/ai/com/l2jserver/game/ai/MonsterAI.java new file mode 100644 index 000000000..ad1ace790 --- /dev/null +++ b/data/script/ai/com/l2jserver/game/ai/MonsterAI.java @@ -0,0 +1,38 @@ +/* + * This file is part of l2jserver . + * + * l2jserver is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * l2jserver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with l2jserver. If not, see . + */ +package com.l2jserver.game.ai; + +import com.l2jserver.game.ai.desires.Desire; +import com.l2jserver.model.world.NPC; + +/** + * @author Rogiel + * + */ +public class MonsterAI extends NPCAI { + /** + * @param creature + */ + protected MonsterAI(NPC creature) { + super(creature); + } + + @Override + protected void handleDesire(Desire desire) { + super.handleDesire(desire); + } +} diff --git a/data/script/ai/com/l2jserver/game/ai/NPCAI.java b/data/script/ai/com/l2jserver/game/ai/NPCAI.java new file mode 100644 index 000000000..6b68a3f1d --- /dev/null +++ b/data/script/ai/com/l2jserver/game/ai/NPCAI.java @@ -0,0 +1,47 @@ +/* + * This file is part of l2jserver . + * + * l2jserver is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * l2jserver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with l2jserver. If not, see . + */ +package com.l2jserver.game.ai; + +import com.google.inject.Inject; +import com.l2jserver.game.ai.desires.Desire; +import com.l2jserver.game.ai.desires.MoveDesire; +import com.l2jserver.model.world.NPC; +import com.l2jserver.service.game.npc.NPCService; + +/** + * @author Rogiel + * + */ +public class NPCAI extends AI { + @Inject + protected NPCService npcService; + + /** + * @param npc + * the npc + */ + protected NPCAI(NPC npc) { + super(npc); + } + + @Override + protected void handleDesire(Desire desire) { + if (desire instanceof MoveDesire) { + + } + } +} diff --git a/src/dao/com/l2jserver/db/dao/h2/H2CharacterDAO.java b/src/dao/com/l2jserver/db/dao/h2/H2CharacterDAO.java index 1d8f08317..7db78820a 100644 --- a/src/dao/com/l2jserver/db/dao/h2/H2CharacterDAO.java +++ b/src/dao/com/l2jserver/db/dao/h2/H2CharacterDAO.java @@ -16,6 +16,7 @@ */ package com.l2jserver.db.dao.h2; +import com.google.inject.Inject; import com.l2jserver.db.dao.CharacterDAO; import com.l2jserver.db.dao.jdbc.JDBCCharacterDAO; import com.l2jserver.model.id.object.provider.CharacterIDProvider; @@ -31,6 +32,7 @@ import com.l2jserver.service.database.DatabaseService; */ public class H2CharacterDAO extends JDBCCharacterDAO implements CharacterDAO { + @Inject public H2CharacterDAO(DatabaseService database, CharacterIDProvider idFactory, CharacterTemplateIDProvider templateIdFactory, diff --git a/src/dao/com/l2jserver/db/dao/h2/H2CharacterFriendDAO.java b/src/dao/com/l2jserver/db/dao/h2/H2CharacterFriendDAO.java index 1db2638ab..e81ffe587 100644 --- a/src/dao/com/l2jserver/db/dao/h2/H2CharacterFriendDAO.java +++ b/src/dao/com/l2jserver/db/dao/h2/H2CharacterFriendDAO.java @@ -16,6 +16,7 @@ */ package com.l2jserver.db.dao.h2; +import com.google.inject.Inject; import com.l2jserver.db.dao.CharacterFriendDAO; import com.l2jserver.db.dao.jdbc.JDBCCharacterFriendDAO; import com.l2jserver.model.id.object.provider.CharacterIDProvider; @@ -29,6 +30,7 @@ import com.l2jserver.service.database.DatabaseService; */ public class H2CharacterFriendDAO extends JDBCCharacterFriendDAO implements CharacterFriendDAO { + @Inject public H2CharacterFriendDAO(DatabaseService database, FriendIDProvider idProvider, CharacterIDProvider charIdProvider) { super(database, idProvider, charIdProvider); diff --git a/src/dao/com/l2jserver/db/dao/h2/H2ClanDAO.java b/src/dao/com/l2jserver/db/dao/h2/H2ClanDAO.java index c65a25b67..e3e353fda 100644 --- a/src/dao/com/l2jserver/db/dao/h2/H2ClanDAO.java +++ b/src/dao/com/l2jserver/db/dao/h2/H2ClanDAO.java @@ -16,6 +16,7 @@ */ package com.l2jserver.db.dao.h2; +import com.google.inject.Inject; import com.l2jserver.db.dao.CharacterDAO; import com.l2jserver.db.dao.ClanDAO; import com.l2jserver.db.dao.jdbc.JDBCClanDAO; @@ -29,6 +30,7 @@ import com.l2jserver.service.database.DatabaseService; * @author Rogiel */ public class H2ClanDAO extends JDBCClanDAO implements ClanDAO { + @Inject public H2ClanDAO(DatabaseService database, ClanIDProvider clanIdFactory, CharacterIDProvider idFactory) { super(database, clanIdFactory, idFactory); diff --git a/src/dao/com/l2jserver/db/dao/h2/H2ItemDAO.java b/src/dao/com/l2jserver/db/dao/h2/H2ItemDAO.java index 7d0bbca49..3737f836a 100644 --- a/src/dao/com/l2jserver/db/dao/h2/H2ItemDAO.java +++ b/src/dao/com/l2jserver/db/dao/h2/H2ItemDAO.java @@ -16,6 +16,7 @@ */ package com.l2jserver.db.dao.h2; +import com.google.inject.Inject; import com.l2jserver.db.dao.ItemDAO; import com.l2jserver.db.dao.jdbc.JDBCItemDAO; import com.l2jserver.model.id.object.provider.CharacterIDProvider; @@ -29,6 +30,7 @@ import com.l2jserver.service.database.DatabaseService; * @author Rogiel */ public class H2ItemDAO extends JDBCItemDAO implements ItemDAO { + @Inject public H2ItemDAO(DatabaseService database, ItemIDProvider idFactory, ItemTemplateIDProvider templateIdFactory, CharacterIDProvider charIdFactory) { diff --git a/src/dao/com/l2jserver/db/dao/h2/H2NPCDAO.java b/src/dao/com/l2jserver/db/dao/h2/H2NPCDAO.java index 6c63ffe31..e49bd03d0 100644 --- a/src/dao/com/l2jserver/db/dao/h2/H2NPCDAO.java +++ b/src/dao/com/l2jserver/db/dao/h2/H2NPCDAO.java @@ -16,6 +16,7 @@ */ package com.l2jserver.db.dao.h2; +import com.google.inject.Inject; import com.l2jserver.db.dao.CharacterDAO; import com.l2jserver.db.dao.NPCDAO; import com.l2jserver.db.dao.jdbc.JDBCNPCDAO; @@ -29,6 +30,7 @@ import com.l2jserver.service.database.DatabaseService; * @author Rogiel */ public class H2NPCDAO extends JDBCNPCDAO implements NPCDAO { + @Inject public H2NPCDAO(DatabaseService database, NPCIDProvider idProvider, NPCTemplateIDProvider templateIdProvider) { super(database, idProvider, templateIdProvider); diff --git a/src/main/java/com/l2jserver/game/ai/AI.java b/src/main/java/com/l2jserver/game/ai/AI.java index 05c9bc3e8..fd3be3286 100644 --- a/src/main/java/com/l2jserver/game/ai/AI.java +++ b/src/main/java/com/l2jserver/game/ai/AI.java @@ -16,9 +16,11 @@ */ package com.l2jserver.game.ai; +import com.google.inject.Inject; import com.l2jserver.game.ai.desires.Desire; import com.l2jserver.game.ai.desires.DesireQueue; import com.l2jserver.model.world.Actor; +import com.l2jserver.service.game.world.event.WorldEventDispatcher; /** * @author Rogiel @@ -26,14 +28,35 @@ import com.l2jserver.model.world.Actor; * the {@link Actor} type for this {@link AI} */ public abstract class AI { + /** + * The desire queue for this AI + */ protected DesireQueue desireQueue = new DesireQueue(); - protected final T creature; + /** + * The actor controlled by this AI + */ + protected final T actor; - protected AI(T creature) { - this.creature = creature; + @Inject + protected WorldEventDispatcher eventDispatcher; + + protected AI(T actor) { + this.actor = actor; } - protected void handleDesire(Desire desire) { - desire.handleDesire(this); + /** + * Executes an AI tick + */ + protected void tick() { + Desire desire = desireQueue.poll(); + handleDesire(desire); } + + /** + * Handles the given desire + * + * @param desire + * the desire + */ + protected abstract void handleDesire(Desire desire); } diff --git a/src/main/java/com/l2jserver/game/ai/desires/AttackDesire.java b/src/main/java/com/l2jserver/game/ai/desires/AttackDesire.java index 181a732f6..aaee34c25 100644 --- a/src/main/java/com/l2jserver/game/ai/desires/AttackDesire.java +++ b/src/main/java/com/l2jserver/game/ai/desires/AttackDesire.java @@ -16,7 +16,6 @@ */ package com.l2jserver.game.ai.desires; -import com.l2jserver.game.ai.AI; import com.l2jserver.model.world.Actor; /** @@ -43,11 +42,6 @@ public final class AttackDesire extends AbstractDesire { this.target = target; } - @Override - public void handleDesire(AI ai) { - // TODO: Implement - } - @Override public boolean equals(Object o) { if (this == o) diff --git a/src/main/java/com/l2jserver/game/ai/desires/Desire.java b/src/main/java/com/l2jserver/game/ai/desires/Desire.java index 1f35cc309..ab867079e 100644 --- a/src/main/java/com/l2jserver/game/ai/desires/Desire.java +++ b/src/main/java/com/l2jserver/game/ai/desires/Desire.java @@ -16,14 +16,8 @@ */ package com.l2jserver.game.ai.desires; -import com.l2jserver.game.ai.AI; - /** - * This interface represents basic desire functions.
- * Each desire should implement {@link #handleDesire(com.l2jserver.game.ai.AI)} - * method with default behavior.
- * AI can override {@link com.l2jserver.game.ai.AI#handleDesire(Desire)} to - * implement custom behavior of desire.
+ * This interface represents basic desire functions. * * @author Rogiel * @see com.l2jserver.game.ai.AI @@ -31,15 +25,6 @@ import com.l2jserver.game.ai.AI; * @see com.l2jserver.game.ai.desires.AbstractDesire */ public interface Desire extends Comparable { - /** - * Invokes default desire action. AI can override invocation of this method - * to handle desire in it's own way - * - * @param ai - * actor that is doing this desire - */ - void handleDesire(AI ai); - /** * Returns hashcode for this object, must be overrided by child * diff --git a/src/main/java/com/l2jserver/game/ai/desires/MoveDesire.java b/src/main/java/com/l2jserver/game/ai/desires/MoveDesire.java index 47af94c8d..f0744fe5c 100644 --- a/src/main/java/com/l2jserver/game/ai/desires/MoveDesire.java +++ b/src/main/java/com/l2jserver/game/ai/desires/MoveDesire.java @@ -16,7 +16,6 @@ */ package com.l2jserver.game.ai.desires; -import com.l2jserver.game.ai.AI; import com.l2jserver.model.world.Actor; import com.l2jserver.util.geometry.Point3D; @@ -44,14 +43,6 @@ public final class MoveDesire extends AbstractDesire { this.point = point; } - @Override - public void handleDesire(AI ai) { - // TODO: Implement - } - - /** - * {@inheritDoc} - */ @Override public boolean equals(Object o) { if (this == o) @@ -64,9 +55,6 @@ public final class MoveDesire extends AbstractDesire { return point.equals(that.point); } - /** - * {@inheritDoc} - */ @Override public int hashCode() { return point.hashCode(); diff --git a/src/main/java/com/l2jserver/game/net/packet/client/CharacterActionPacket.java b/src/main/java/com/l2jserver/game/net/packet/client/CharacterActionPacket.java index 85e25437b..93e7344a9 100644 --- a/src/main/java/com/l2jserver/game/net/packet/client/CharacterActionPacket.java +++ b/src/main/java/com/l2jserver/game/net/packet/client/CharacterActionPacket.java @@ -25,6 +25,7 @@ import com.l2jserver.model.id.ObjectID; import com.l2jserver.model.id.object.NPCID; import com.l2jserver.model.id.object.provider.ObjectIDResolver; import com.l2jserver.model.world.NPC; +import com.l2jserver.service.game.character.CannotSetTargetServiceException; import com.l2jserver.service.game.npc.ActionServiceException; import com.l2jserver.service.game.npc.NPCService; import com.l2jserver.util.geometry.Coordinate; @@ -97,6 +98,8 @@ public class CharacterActionPacket extends AbstractClientPacket { npcService.action(npc, conn.getCharacter(), action); } catch (ActionServiceException e) { conn.sendActionFailed(); + } catch (CannotSetTargetServiceException e) { + conn.sendActionFailed(); } } } diff --git a/src/main/java/com/l2jserver/game/net/packet/client/CharacterRequestBypass.java b/src/main/java/com/l2jserver/game/net/packet/client/CharacterRequestBypass.java index 3e076b91a..1dbc07dd8 100644 --- a/src/main/java/com/l2jserver/game/net/packet/client/CharacterRequestBypass.java +++ b/src/main/java/com/l2jserver/game/net/packet/client/CharacterRequestBypass.java @@ -27,6 +27,7 @@ import com.l2jserver.model.id.ObjectID; import com.l2jserver.model.id.object.NPCID; import com.l2jserver.model.id.object.provider.ObjectIDResolver; import com.l2jserver.model.world.NPC; +import com.l2jserver.service.game.character.CannotSetTargetServiceException; import com.l2jserver.service.game.npc.ActionServiceException; import com.l2jserver.service.game.npc.NPCService; import com.l2jserver.util.BufferUtils; @@ -86,6 +87,8 @@ public class CharacterRequestBypass extends AbstractClientPacket { createArgumentArray(tokenizer)); } catch (ActionServiceException e) { conn.sendActionFailed(); + } catch (CannotSetTargetServiceException e) { + conn.sendActionFailed(); } } } diff --git a/src/main/java/com/l2jserver/model/world/NPC.java b/src/main/java/com/l2jserver/model/world/NPC.java index aa5336c73..227056964 100644 --- a/src/main/java/com/l2jserver/model/world/NPC.java +++ b/src/main/java/com/l2jserver/model/world/NPC.java @@ -29,6 +29,12 @@ import com.l2jserver.service.game.ai.AIScript; * @author Rogiel */ public class NPC extends Actor { + private NPCState state; + + public enum NPCState { + MOVING, ATTACKING; + } + /** * Creates a new instance * @@ -39,6 +45,42 @@ public class NPC extends Actor { super(templateID); } + /** + * @return the state + */ + public NPCState getState() { + return state; + } + + /** + * @return true if NPC is idle + */ + public boolean isIdle() { + return state == null; + } + + /** + * @return true if NPC is idle + */ + public boolean isMoving() { + return state == NPCState.MOVING; + } + + /** + * @return true if NPC is idle + */ + public boolean isAttacking() { + return state == NPCState.ATTACKING; + } + + /** + * @param state + * the state to set + */ + public void setState(NPCState state) { + this.state = state; + } + @Override public ActorSex getSex() { return this.getTemplate().getSex(); diff --git a/src/main/java/com/l2jserver/service/ServiceModule.java b/src/main/java/com/l2jserver/service/ServiceModule.java index d2669b98a..19514c79d 100644 --- a/src/main/java/com/l2jserver/service/ServiceModule.java +++ b/src/main/java/com/l2jserver/service/ServiceModule.java @@ -25,6 +25,8 @@ import com.l2jserver.service.configuration.ConfigurationService; import com.l2jserver.service.configuration.ProxyConfigurationService; import com.l2jserver.service.core.Log4JLoggingService; import com.l2jserver.service.core.LoggingService; +import com.l2jserver.service.core.threading.ThreadService; +import com.l2jserver.service.core.threading.ThreadServiceImpl; import com.l2jserver.service.core.vfs.VFSService; import com.l2jserver.service.core.vfs.VFSServiceImpl; import com.l2jserver.service.database.DatabaseService; @@ -66,9 +68,12 @@ public class ServiceModule extends AbstractModule { bind(LoggingService.class).to(Log4JLoggingService.class).in( Scopes.SINGLETON); bind(VFSService.class).to(VFSServiceImpl.class).in(Scopes.SINGLETON); + bind(ThreadService.class).to(ThreadServiceImpl.class).in( + Scopes.SINGLETON); bind(ConfigurationService.class).to(ProxyConfigurationService.class) .in(Scopes.SINGLETON); bind(CacheService.class).to(EhCacheService.class).in(Scopes.SINGLETON); + bind(DatabaseService.class).to(JDBCDatabaseService.class).in( Scopes.SINGLETON); bind(WorldIDService.class).to(CachedWorldIDService.class).in( diff --git a/src/main/java/com/l2jserver/service/cache/EhCacheService.java b/src/main/java/com/l2jserver/service/cache/EhCacheService.java index 07d19e95d..88ebf2c4c 100644 --- a/src/main/java/com/l2jserver/service/cache/EhCacheService.java +++ b/src/main/java/com/l2jserver/service/cache/EhCacheService.java @@ -200,7 +200,7 @@ public class EhCacheService extends AbstractService implements CacheService { final Element element = cache.get(key); if (element == null) return null; - return (V) element.getValue(); + return (V) element.getObjectValue(); } @Override diff --git a/src/main/java/com/l2jserver/service/core/threading/ScheduledAsyncFuture.java b/src/main/java/com/l2jserver/service/core/threading/ScheduledAsyncFuture.java new file mode 100644 index 000000000..cc864c866 --- /dev/null +++ b/src/main/java/com/l2jserver/service/core/threading/ScheduledAsyncFuture.java @@ -0,0 +1,29 @@ +/* + * This file is part of l2jserver . + * + * l2jserver is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * l2jserver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with l2jserver. If not, see . + */ +package com.l2jserver.service.core.threading; + +import java.util.concurrent.ScheduledFuture; + +/** + * This future instance extends {@link ScheduledFuture}. An scheduled future + * cannot return values neither await for its termination because its execution + * is continuously repeated. + * + * @author Rogiel + */ +public interface ScheduledAsyncFuture extends ScheduledFuture { +} diff --git a/src/main/java/com/l2jserver/service/core/threading/ThreadPool.java b/src/main/java/com/l2jserver/service/core/threading/ThreadPool.java new file mode 100644 index 000000000..2b84a5266 --- /dev/null +++ b/src/main/java/com/l2jserver/service/core/threading/ThreadPool.java @@ -0,0 +1,71 @@ +/* + * This file is part of l2jserver . + * + * l2jserver is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * l2jserver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with l2jserver. If not, see . + */ +package com.l2jserver.service.core.threading; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +/** + * This is an ThreadPool you can use it to schedule tasks in the future or to + * repeat them many times. + * + * @author Rogiel + */ +public interface ThreadPool { + /** + * Executes an asynchronous tasks. + * + * @param + * the task return type + * @param callable + * the callable instance + * @return the {@link AsyncFuture} notified once the task has completed + */ + AsyncFuture async(Callable callable); + + /** + * Executes an asynchronous tasks at an scheduled time. + * + * @param + * the task return type + * @param callable + * the callable instance + * @param delay + * the initial delay to wait before the task is executed + * @param unit + * the time unit of delay + * @return the {@link AsyncFuture} notified once the task has completed + */ + AsyncFuture async(long delay, TimeUnit unit, Callable callable); + + /** + * Executes an asynchronous tasks at an scheduled time. + * + * @param callable + * the callable instance + * @param delay + * the initial delay to wait before the task is executed + * @param repeat + * the repeating interval for this task + * @param unit + * the time unit of delay + * @return the {@link AsyncFuture} notified once the task has completed + */ + ScheduledAsyncFuture async(long delay, TimeUnit unit, long repeat, + Runnable task); + +} diff --git a/src/main/java/com/l2jserver/service/core/threading/ThreadService.java b/src/main/java/com/l2jserver/service/core/threading/ThreadService.java index 69a922182..6c0ca6989 100644 --- a/src/main/java/com/l2jserver/service/core/threading/ThreadService.java +++ b/src/main/java/com/l2jserver/service/core/threading/ThreadService.java @@ -30,6 +30,8 @@ import com.l2jserver.service.Service; public interface ThreadService extends Service { /** * Executes an asynchronous tasks. + *

+ * Tasks scheduled here will go to an default shared thread pool. * * @param * the task return type @@ -43,6 +45,8 @@ public interface ThreadService extends Service { * Executes an asynchronous tasks at an scheduled time. Please note that * resources in scheduled thread pool are limited and tasks should be * performed fast. + *

+ * Tasks scheduled here will go to an default shared thread pool. * * @param * the task return type @@ -55,4 +59,44 @@ public interface ThreadService extends Service { * @return the {@link AsyncFuture} notified once the task has completed */ AsyncFuture async(long delay, TimeUnit unit, Callable callable); + + /** + * Executes an asynchronous tasks at an scheduled time. Please note that + * resources in scheduled thread pool are limited and tasks should be + * performed fast. + *

+ * Tasks scheduled here will go to an default shared thread pool. + * + * @param callable + * the callable instance + * @param delay + * the initial delay to wait before the task is executed + * @param repeat + * the repeating interval for this task + * @param unit + * the time unit of delay + * @return the {@link AsyncFuture} notified once the task has completed + */ + ScheduledAsyncFuture async(long delay, TimeUnit unit, long repeat, + Runnable task); + + /** + * Creates a new thread pool. + * + * @param name + * the pool name + * @param maxThreads + * the maximum amount of threads. + * @return the new thread pool + */ + ThreadPool createThreadPool(String name, int maxThreads); + + /** + * Disposes an given thread pool. After disposing the thread pool will no + * longer be usable. + * + * @param pool + * the thread pool to be disposed + */ + void dispose(ThreadPool pool); } diff --git a/src/main/java/com/l2jserver/service/core/threading/ThreadServiceImpl.java b/src/main/java/com/l2jserver/service/core/threading/ThreadServiceImpl.java index 4bb3c36bf..fcb06e31b 100644 --- a/src/main/java/com/l2jserver/service/core/threading/ThreadServiceImpl.java +++ b/src/main/java/com/l2jserver/service/core/threading/ThreadServiceImpl.java @@ -17,11 +17,12 @@ package com.l2jserver.service.core.threading; import java.util.concurrent.Callable; +import java.util.concurrent.Delayed; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -44,25 +45,19 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService */ private final Logger log = LoggerFactory.getLogger(this.getClass()); - /** - * The scheduler Thread pool - */ - private ScheduledExecutorService scheduler; - /** - * The asynchronous Thread pool - */ - private ExecutorService async; + private ThreadPool pool; @Override protected void doStart() throws ServiceStartException { - scheduler = Executors.newScheduledThreadPool(10); - async = Executors.newCachedThreadPool(); + pool = createThreadPool("shared", 20); + // scheduler = Executors.newScheduledThreadPool(10); + // async = Executors.newCachedThreadPool(); } @Override public AsyncFuture async(Callable callable) { Preconditions.checkNotNull(callable, "callable"); - return new AsyncFutureImpl(async.submit(callable)); + return pool.async(callable); } @Override @@ -71,25 +66,37 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService Preconditions.checkArgument(delay >= 0, "delay < 0"); Preconditions.checkNotNull(unit, "unit"); Preconditions.checkNotNull(callable, "callable"); - return new AsyncFutureImpl(scheduler.schedule(callable, delay, unit)); + return pool.async(delay, unit, callable); + } + + @Override + public ScheduledAsyncFuture async(long delay, TimeUnit unit, long repeat, + Runnable task) { + Preconditions.checkArgument(delay >= 0, "delay < 0"); + Preconditions.checkArgument(repeat >= 0, "repeat < 0"); + Preconditions.checkNotNull(unit, "unit"); + Preconditions.checkNotNull(task, "task"); + return pool.async(delay, unit, repeat, task); + } + + @Override + public ThreadPool createThreadPool(String name, int maxThreads) { + return new ThreadPoolImpl(name, + Executors.newScheduledThreadPool(maxThreads)); + } + + @Override + public void dispose(ThreadPool pool) { + if (pool instanceof ThreadPoolImpl) + ((ThreadPoolImpl) pool).executor.shutdown(); + throw new UnsupportedOperationException( + "The given ThreadPool is not supported by this service"); } @Override protected void doStop() throws ServiceStopException { - scheduler.shutdown(); - async.shutdown(); - try { - scheduler.awaitTermination(60, TimeUnit.SECONDS); - } catch (InterruptedException e) { - log.warn("Scheduler thread did not stop in 60 seconds", e); - } - try { - async.awaitTermination(60, TimeUnit.SECONDS); - } catch (InterruptedException e) { - log.warn("Asynchronous thread did not stop in 60 seconds", e); - } - scheduler = null; - async = null; + dispose(pool); + pool = null; } /** @@ -99,7 +106,7 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService * @param * the return type */ - public static class AsyncFutureImpl implements AsyncFuture { + private class AsyncFutureImpl implements AsyncFuture { /** * The future that is delegated in this implementation */ @@ -184,4 +191,92 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService } } } + + private class ScheduledAsyncFutureImpl implements ScheduledAsyncFuture { + private final ScheduledFuture future; + + public ScheduledAsyncFutureImpl(ScheduledFuture future) { + this.future = future; + } + + @Override + public long getDelay(TimeUnit unit) { + return future.getDelay(unit); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return future.cancel(mayInterruptIfRunning); + } + + @Override + public int compareTo(Delayed o) { + return future.compareTo(o); + } + + @Override + public boolean isCancelled() { + return future.isCancelled(); + } + + @Override + public boolean isDone() { + return future.isDone(); + } + + @Override + public Object get() throws InterruptedException, ExecutionException { + throw new UnsupportedOperationException(); + } + + @Override + public Object get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, + TimeoutException { + throw new UnsupportedOperationException(); + } + + } + + private class ThreadPoolImpl implements ThreadPool { + /** + * This thread pool name + */ + private final String name; + /** + * The backing executor + */ + private final ScheduledExecutorService executor; + + public ThreadPoolImpl(String name, ScheduledExecutorService executor) { + this.name = name; + this.executor = executor; + } + + @Override + public AsyncFuture async(Callable callable) { + log.debug("Task {} submited to {}", callable, name); + return new AsyncFutureImpl(executor.submit(callable)); + } + + @Override + public AsyncFuture async(long delay, TimeUnit unit, + Callable callable) { + if (log.isDebugEnabled()) + log.debug("Task {} scheduled in {} {} to {}", new Object[] { + callable, delay, unit, name }); + return new AsyncFutureImpl(executor.schedule(callable, delay, + unit)); + } + + @Override + public ScheduledAsyncFuture async(long delay, TimeUnit unit, + long repeat, Runnable task) { + if (log.isDebugEnabled()) + log.debug("Task {} scheduled every {} {} to {}", new Object[] { + task, repeat, unit, name }); + return new ScheduledAsyncFutureImpl(executor.scheduleAtFixedRate( + task, delay, repeat, unit)); + } + } } diff --git a/src/main/java/com/l2jserver/service/game/npc/NPCService.java b/src/main/java/com/l2jserver/service/game/npc/NPCService.java index e6e63ae37..68e627f3b 100644 --- a/src/main/java/com/l2jserver/service/game/npc/NPCService.java +++ b/src/main/java/com/l2jserver/service/game/npc/NPCService.java @@ -24,8 +24,11 @@ import com.l2jserver.model.template.NPCTemplate; import com.l2jserver.model.world.L2Character; import com.l2jserver.model.world.NPC; import com.l2jserver.service.Service; +import com.l2jserver.service.core.threading.AsyncFuture; +import com.l2jserver.service.game.character.CannotSetTargetServiceException; import com.l2jserver.service.game.spawn.AlreadySpawnedServiceException; import com.l2jserver.service.game.spawn.SpawnPointNotFoundServiceException; +import com.l2jserver.util.geometry.Point3D; /** * This service manages {@link NPC} instances @@ -43,9 +46,11 @@ public interface NPCService extends Service { * the character * @param action * the action type + * @throws CannotSetTargetServiceException + * if was not possible to set the target */ void action(NPC npc, L2Character character, CharacterAction action) - throws ActionServiceException; + throws ActionServiceException, CannotSetTargetServiceException; /** * Executes an action for an NPC. Each {@link NPCTemplate} have it's own @@ -57,9 +62,22 @@ public interface NPCService extends Service { * the character * @param args * the action arguments + * @throws CannotSetTargetServiceException + * if was not possible to set the target */ void action(NPC npc, L2Character character, String... args) - throws ActionServiceException; + throws ActionServiceException, CannotSetTargetServiceException; + + /** + * Moves an given npc to an point + * + * @param npc + * the NPC + * @param point + * the destination point + * @return the future informing once the NPC has been moved to that location + */ + AsyncFuture move(NPC npc, Point3D point); /** * Load from database and spawn all {@link NPC NPCs} instances diff --git a/src/main/java/com/l2jserver/service/game/npc/NPCServiceImpl.java b/src/main/java/com/l2jserver/service/game/npc/NPCServiceImpl.java index 045b2c58a..6faabbdb3 100644 --- a/src/main/java/com/l2jserver/service/game/npc/NPCServiceImpl.java +++ b/src/main/java/com/l2jserver/service/game/npc/NPCServiceImpl.java @@ -18,6 +18,8 @@ package com.l2jserver.service.game.npc; import java.util.Collection; import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; import com.google.common.base.Preconditions; import com.google.inject.Inject; @@ -27,10 +29,16 @@ import com.l2jserver.game.net.Lineage2Connection; import com.l2jserver.game.net.packet.client.CharacterActionPacket.CharacterAction; import com.l2jserver.game.net.packet.server.ActorAttackPacket; import com.l2jserver.model.server.AttackHit; +import com.l2jserver.model.server.AttackHit.AttackHitFlag; import com.l2jserver.model.world.L2Character; import com.l2jserver.model.world.NPC; +import com.l2jserver.model.world.NPC.NPCState; import com.l2jserver.model.world.npc.controller.NPCController; import com.l2jserver.service.AbstractService; +import com.l2jserver.service.AbstractService.Depends; +import com.l2jserver.service.core.threading.AsyncFuture; +import com.l2jserver.service.core.threading.ThreadService; +import com.l2jserver.service.game.character.CannotSetTargetServiceException; import com.l2jserver.service.game.character.CharacterService; import com.l2jserver.service.game.spawn.AlreadySpawnedServiceException; import com.l2jserver.service.game.spawn.SpawnPointNotFoundServiceException; @@ -38,12 +46,15 @@ import com.l2jserver.service.game.spawn.SpawnService; import com.l2jserver.service.network.NetworkService; import com.l2jserver.util.exception.L2Exception; import com.l2jserver.util.factory.CollectionFactory; +import com.l2jserver.util.geometry.Point3D; /** * Default {@link NPCService} implementation * * @author Rogiel */ +@Depends({ SpawnService.class, NetworkService.class, CharacterService.class, + ThreadService.class }) public class NPCServiceImpl extends AbstractService implements NPCService { /** * The {@link SpawnService} used to spawn the {@link NPC} instances @@ -56,8 +67,11 @@ public class NPCServiceImpl extends AbstractService implements NPCService { /** * The {@link CharacterService} */ - @SuppressWarnings("unused") private final CharacterService characterService; + /** + * The {@link ThreadService} + */ + private final ThreadService threadService; /** * The {@link NPCDAO} @@ -79,21 +93,25 @@ public class NPCServiceImpl extends AbstractService implements NPCService { @Inject public NPCServiceImpl(SpawnService spawnService, NetworkService networkService, CharacterService characterService, - NPCDAO npcDao, Injector injector) { + ThreadService threadService, NPCDAO npcDao, Injector injector) { this.spawnService = spawnService; this.networkService = networkService; this.characterService = characterService; + this.threadService = threadService; this.npcDao = npcDao; this.injector = injector; } @Override public void action(NPC npc, L2Character character, CharacterAction action) - throws ActionServiceException { + throws ActionServiceException, CannotSetTargetServiceException { Preconditions.checkNotNull(npc, "npc"); Preconditions.checkNotNull(character, "character"); Preconditions.checkNotNull(action, "action"); + if (npc.getTemplate().isTargetable()) + characterService.target(character, npc); + final Lineage2Connection conn = networkService.discover(character .getID()); try { @@ -106,12 +124,15 @@ public class NPCServiceImpl extends AbstractService implements NPCService { @Override public void action(NPC npc, L2Character character, String... args) - throws ActionServiceException { + throws ActionServiceException, CannotSetTargetServiceException { Preconditions.checkNotNull(npc, "npc"); Preconditions.checkNotNull(character, "character"); if (args == null) args = new String[0]; + if (npc.getTemplate().isTargetable()) + characterService.target(character, npc); + final Lineage2Connection conn = networkService.discover(character .getID()); try { @@ -123,7 +144,30 @@ public class NPCServiceImpl extends AbstractService implements NPCService { } @Override - public Collection spawnAll() throws SpawnPointNotFoundServiceException, + public AsyncFuture move(final NPC npc, final Point3D point) { + if (!npc.isIdle()) + // TODO throw an exception + return null; + npc.setState(NPCState.MOVING); + // calculate walking time + final Point3D start = npc.getPoint(); + final double distance = start.getDistance(point); + final double seconds = distance / npc.getTemplate().getRunSpeed(); + // TODO this is an dirty implementation! + return threadService.async((int) (seconds * 1000), + TimeUnit.MILLISECONDS, new Callable() { + @Override + public Boolean call() throws Exception { + npc.setState(null); + npc.setPoint(point); + return false; + } + }); + } + + @Override + public Collection spawnAll() + throws SpawnPointNotFoundServiceException, AlreadySpawnedServiceException { final Collection npcs = npcDao.loadAll(); for (final NPC npc : npcs) { @@ -140,7 +184,8 @@ public class NPCServiceImpl extends AbstractService implements NPCService { Preconditions.checkNotNull(attacker, "attacker"); conn.write(new ActorAttackPacket(conn.getCharacter(), new AttackHit( - conn.getCharacter(), npc))); + conn.getCharacter(), npc, AttackHitFlag.MISS, + AttackHitFlag.SOULSHOT))); } private NPCController getController(NPC npc) { diff --git a/src/main/java/com/l2jserver/service/game/world/event/WorldEventDispatcherImpl.java b/src/main/java/com/l2jserver/service/game/world/event/WorldEventDispatcherImpl.java index e68cbbcaa..e692d5e2a 100644 --- a/src/main/java/com/l2jserver/service/game/world/event/WorldEventDispatcherImpl.java +++ b/src/main/java/com/l2jserver/service/game/world/event/WorldEventDispatcherImpl.java @@ -19,7 +19,6 @@ package com.l2jserver.service.game.world.event; import java.util.Map; import java.util.Queue; import java.util.Set; -import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -30,8 +29,11 @@ import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.AbstractFuture; +import com.google.inject.Inject; import com.l2jserver.model.id.ObjectID; import com.l2jserver.model.world.WorldObject; +import com.l2jserver.service.core.threading.ThreadPool; +import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.util.factory.CollectionFactory; /** @@ -46,10 +48,12 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher { private static final Logger log = LoggerFactory .getLogger(WorldEventDispatcherImpl.class); + private final ThreadService threadService; + /** * The execution thread */ - private Timer timer; + private ThreadPool threadPool; /** * The list of all global listeners @@ -66,10 +70,14 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher { private Queue events = CollectionFactory .newConcurrentQueue(); + @Inject + public WorldEventDispatcherImpl(ThreadService threadService) { + this.threadService = threadService; + } + public void start() { - // TODO event dispatching should be done using ThreadService - timer = new Timer(); - final TimerTask task = new TimerTask() { + threadPool = threadService.createThreadPool("event-dispatcher", 1); + threadPool.async(0, TimeUnit.MILLISECONDS, 20, new TimerTask() { @Override public void run() { EventContainer event; @@ -91,8 +99,7 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher { } } } - }; - timer.scheduleAtFixedRate(task, 0, 50); + }); } @Override @@ -223,8 +230,8 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher { } public void stop() { - timer.cancel(); - timer = null; + threadService.dispose(threadPool); + threadPool = null; } /**