diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadPool.java b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadPool.java index 9e20b5d48..b57c91315 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadPool.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadPool.java @@ -19,8 +19,7 @@ package com.l2jserver.service.core.threading; 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. + * This is an ThreadPool that you can use to asynchronously execute tasks. * * @author Rogiel */ @@ -72,4 +71,9 @@ public interface ThreadPool { * execute tasks. */ void dispose(); + + /** + * @return true if the thread pool is no longer usable (i.e. was disposed) + */ + boolean isDisposed(); } diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadPoolPriority.java b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadPoolPriority.java new file mode 100644 index 000000000..3d4e80db9 --- /dev/null +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadPoolPriority.java @@ -0,0 +1,55 @@ +/* + * This file is part of l2jserver2 . + * + * l2jserver2 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. + * + * l2jserver2 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 l2jserver2. If not, see . + */ +package com.l2jserver.service.core.threading; + +/** + * The priority of the thread pool + * + * @author Rogiel + */ +public enum ThreadPoolPriority { + /** + * High priority. + *

+ * Processor will block {@link ThreadPoolPriority#NORMAL} and + * {@link ThreadPoolPriority#LOW} priority threads in order to finish tasks + * in pools on this priority. + */ + HIGH(Thread.MAX_PRIORITY), + /** + * Normal priority. + *

+ * Processor will block {@link ThreadPoolPriority#LOW} priority threads in + * order to finish tasks in pools on this priority. + */ + NORMAL(Thread.NORM_PRIORITY), + /** + * Low priority. + *

+ * Processor will give very low priority for tasks in this level. + */ + LOW(Thread.MIN_PRIORITY); + + /** + * The priority to be used on {@link Thread} + */ + public final int threadPriority; + + ThreadPoolPriority(int threadPriority) { + this.threadPriority = threadPriority; + } +} diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadService.java b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadService.java index d308ffefc..a93ebe327 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadService.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadService.java @@ -79,16 +79,72 @@ public interface ThreadService extends Service { ScheduledAsyncFuture async(long delay, TimeUnit unit, long repeat, Runnable task); + /** + * Creates a new thread pool with {@link ThreadPoolPriority#NORMAL normal} + * priority and that can be increased up to {@link Integer#MAX_VALUE} active + * threads, if necessary. + *

+ * Threads in this pool will never expire. + * + * @param name + * the pool name + * @param threads + * the maximum amount of active threads + * @return the new thread pool + */ + ThreadPool createThreadPool(String name, int threads); + + /** + * Creates a new thread pool that can increase up to + * {@link Integer#MAX_VALUE} active threads, if necessary. + *

+ * Threads in this pool will never expire. + * + * @param name + * the pool name + * @param threads + * the maximum amount of active threads + * @param priority + * the processor scheduling priority + * @return the new thread pool + */ + ThreadPool createThreadPool(String name, int threads, + ThreadPoolPriority priority); + + /** + * Creates a new thread pool with {@link ThreadPoolPriority#NORMAL normal} + * priority. + * + * @param name + * the pool name + * @param threads + * the maximum amount of active threads + * @param threadTimeout + * the time it takes to expire an inactive thread + * @param threadTimeoutUnit + * the {@link TimeUnit} for threadTimeout + * @return the new thread pool + */ + ThreadPool createThreadPool(String name, int threads, long threadTimeout, + TimeUnit threadTimeoutUnit); + /** * Creates a new thread pool. * * @param name * the pool name - * @param maxThreads - * the maximum amount of threads. + * @param threads + * the maximum amount of active threads + * @param threadTimeout + * the time it takes to expire an inactive thread + * @param threadTimeoutUnit + * the {@link TimeUnit} for threadTimeout + * @param priority + * the processor scheduling priority * @return the new thread pool */ - ThreadPool createThreadPool(String name, int maxThreads); + ThreadPool createThreadPool(String name, int threads, long threadTimeout, + TimeUnit threadTimeoutUnit, ThreadPoolPriority priority); /** * Disposes an given thread pool. After disposing the thread pool will no diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadServiceImpl.java b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadServiceImpl.java index 0e50e1bc6..959b867da 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadServiceImpl.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/core/threading/ThreadServiceImpl.java @@ -22,12 +22,13 @@ import java.util.Map.Entry; 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.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,7 +63,7 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService @Override protected void doStart() throws ServiceStartException { threadPools = CollectionFactory.newMap(); - pool = createThreadPool("shared", 20); + pool = createThreadPool("shared", 1); pool.async(50, TimeUnit.MILLISECONDS, 50, new Runnable() { @Override @@ -111,21 +112,61 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService } @Override - public ThreadPool createThreadPool(String name, int maxThreads) { - log.debug("Creating new ThreadPool {} with maximum of {} threads", - name, maxThreads); - final ThreadPoolImpl pool = new ThreadPoolImpl(name, - Executors.newScheduledThreadPool(maxThreads)); + public ThreadPool createThreadPool(final String name, final int threads, + final long threadTimeout, final TimeUnit threadTimeoutUnit, + final ThreadPoolPriority priority) { + log.debug( + "Creating new {} priority ThreadPool {}; threads: {}, timeout:{}", + new Object[] { priority, name, threads, threadTimeout }); + final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor( + threads); + if (threadTimeout >= 1) { + executor.setKeepAliveTime(threadTimeout, threadTimeoutUnit); + executor.allowCoreThreadTimeOut(true); + } + executor.setThreadFactory(new ThreadFactory() { + private final AtomicInteger threadNumber = new AtomicInteger(1); + + @Override + public Thread newThread(Runnable r) { + final Thread thread = new Thread(r, name + "-" + + threadNumber.getAndIncrement()); + thread.setPriority(priority.threadPriority); + return thread; + } + }); + + final ThreadPoolImpl pool = new ThreadPoolImpl(name, executor); threadPools.put(name, pool); return pool; } + @Override + public ThreadPool createThreadPool(String name, int threads) { + return createThreadPool(name, threads, -1, null, + ThreadPoolPriority.NORMAL); + } + + @Override + public ThreadPool createThreadPool(String name, int threads, + ThreadPoolPriority priority) { + return createThreadPool(name, threads, -1, null, priority); + } + + @Override + public ThreadPool createThreadPool(String name, int threads, + long threadTimeout, TimeUnit threadTimeoutUnit) { + return createThreadPool(name, threads, threadTimeout, + threadTimeoutUnit, ThreadPoolPriority.NORMAL); + } + @Override public void dispose(ThreadPool pool) { log.debug("Disposing ThreadPool {}", pool); if (pool instanceof ThreadPoolImpl) { ((ThreadPoolImpl) pool).executor.shutdown(); threadPools.remove(((ThreadPoolImpl) pool).name); + return; } throw new UnsupportedOperationException( "The given ThreadPool is not supported by this service"); @@ -331,7 +372,7 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService /** * The backing executor */ - private final ScheduledExecutorService executor; + private final ScheduledThreadPoolExecutor executor; /** * The list of active and pending futures */ @@ -342,9 +383,9 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService * @param name * the pool name * @param executor - * the backing {@link ScheduledExecutorService} + * the backing {@link ScheduledThreadPoolExecutor} */ - public ThreadPoolImpl(String name, ScheduledExecutorService executor) { + public ThreadPoolImpl(String name, ScheduledThreadPoolExecutor executor) { this.name = name; this.executor = executor; } @@ -392,5 +433,10 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService } } } + + @Override + public boolean isDisposed() { + return executor.isShutdown(); + } } } diff --git a/l2jserver2-common/src/main/java/com/l2jserver/util/ThreadPoolUtils.java b/l2jserver2-common/src/main/java/com/l2jserver/util/ThreadPoolUtils.java new file mode 100644 index 000000000..1d04826d0 --- /dev/null +++ b/l2jserver2-common/src/main/java/com/l2jserver/util/ThreadPoolUtils.java @@ -0,0 +1,156 @@ +/* + * This file is part of l2jserver2 . + * + * l2jserver2 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. + * + * l2jserver2 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 l2jserver2. If not, see . + */ +package com.l2jserver.util; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.l2jserver.service.core.threading.Task; +import com.l2jserver.service.core.threading.ThreadPool; + +/** + * @author Rogiel + */ +public class ThreadPoolUtils { + /** + * Wraps an {@link ThreadPool} into an {@link Executor} + * + * @param pool + * the pool the to be wrapped + * @return the wrapped {@link Executor} + */ + public static ExecutorService wrap(final ThreadPool pool) { + return new ExecutorService() { + @Override + public void execute(final Runnable command) { + pool.async(wrap(command)); + } + + @Override + public void shutdown() { + pool.dispose(); + } + + @Override + public List shutdownNow() { + pool.dispose(); + return null; + } + + @Override + public boolean isShutdown() { + return pool.isDisposed(); + } + + @Override + public boolean isTerminated() { + return pool.isDisposed(); + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) + throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public Future submit(Callable task) { + return pool.async(wrap(task)); + } + + @Override + public Future submit(Runnable task, T result) { + return pool.async(wrap(Executors.callable(task, result))); + } + + @Override + public Future submit(Runnable task) { + return pool.async(wrap(task)); + } + + @Override + public List> invokeAll( + Collection> tasks) + throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public List> invokeAll( + Collection> tasks, long timeout, + TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public T invokeAny(Collection> tasks) + throws InterruptedException, ExecutionException { + throw new UnsupportedOperationException(); + } + + @Override + public T invokeAny(Collection> tasks, + long timeout, TimeUnit unit) throws InterruptedException, + ExecutionException, TimeoutException { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Wraps an {@link Runnable} into an {@link Task} + * + * @param command + * the {@link Runnable} to be wrapped + * @return the wrapped {@link Runnable} + */ + public static Task wrap(final Runnable command) { + return new Task() { + @Override + public Runnable call() throws Exception { + command.run(); + return command; + } + }; + } + + /** + * Wraps an {@link Runnable} into an {@link Task} + * + * @param + * the {@link Task} return type + * @param command + * the {@link Runnable} to be wrapped + * @return the wrapped {@link Runnable} + */ + public static Task wrap(final Callable command) { + return new Task() { + @Override + public T call() throws Exception { + return command.call(); + } + }; + } +} diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/L2JGameServerMain.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/L2JGameServerMain.java index 4b13f743f..1394ad5fa 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/L2JGameServerMain.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/L2JGameServerMain.java @@ -16,7 +16,9 @@ */ package com.l2jserver; +import com.l2jserver.service.Service; import com.l2jserver.service.ServiceManager; +import com.l2jserver.service.ServiceStopException; import com.l2jserver.service.cache.CacheService; import com.l2jserver.service.configuration.ConfigurationService; import com.l2jserver.service.database.DatabaseService; @@ -37,41 +39,42 @@ import com.l2jserver.service.network.keygen.BlowfishKeygenService; * @author Rogiel */ public class L2JGameServerMain { + public static final Class[] SERVICES = { CacheService.class, + ConfigurationService.class, DatabaseService.class, + WorldIDService.class, ScriptingService.class, + TemplateService.class, ChatService.class, NPCService.class, + ItemService.class, CharacterService.class, PathingService.class, + BlowfishKeygenService.class, NetworkService.class }; + /** * Main method * * @param args * no arguments are used */ + @SuppressWarnings("unchecked") public static void main(String[] args) { final L2JGameServer server = new L2JGameServer(); try { final ServiceManager serviceManager = server.getInjector() .getInstance(ServiceManager.class); - serviceManager.start(CacheService.class); - serviceManager.start(ConfigurationService.class); - serviceManager.start(DatabaseService.class); - serviceManager.start(WorldIDService.class); + for (final Class service : SERVICES) { + serviceManager.start((Class) service); + } - serviceManager.start(ScriptingService.class); - serviceManager.start(TemplateService.class); - - serviceManager.start(ChatService.class); - serviceManager.start(NPCService.class); - serviceManager.start(ItemService.class); - - serviceManager.start(CharacterService.class); - serviceManager.start(PathingService.class); - - serviceManager.start(BlowfishKeygenService.class); - serviceManager.start(NetworkService.class); - - // final long free = Runtime.getRuntime().freeMemory(); - // final long allocated = Runtime.getRuntime().totalMemory(); - // final long maximum = Runtime.getRuntime().maxMemory(); - // final long processors = - // Runtime.getRuntime().availableProcessors(); + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + @Override + public void run() { + for (final Class service : SERVICES) { + try { + serviceManager + .stop((Class) service); + } catch (ServiceStopException e) { + } + } + } + })); } catch (Exception e) { System.out.println("GameServer could not be started!"); e.printStackTrace(); diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/character/event/CharacterStartMovingEvent.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/character/event/CharacterStartMovingEvent.java new file mode 100644 index 000000000..f10044e37 --- /dev/null +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/character/event/CharacterStartMovingEvent.java @@ -0,0 +1,85 @@ +/* + * This file is part of l2jserver2 . + * + * l2jserver2 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. + * + * l2jserver2 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 l2jserver2. If not, see . + */ +package com.l2jserver.model.world.character.event; + +import com.l2jserver.model.id.ObjectID; +import com.l2jserver.model.world.Actor; +import com.l2jserver.model.world.L2Character; +import com.l2jserver.model.world.Player; +import com.l2jserver.model.world.WorldObject; +import com.l2jserver.util.geometry.Point3D; + +/** + * Event triggered once a character moves + * + * @author Rogiel + */ +public class CharacterStartMovingEvent implements CharacterEvent { + /** + * The character that is logging in + */ + private final L2Character character; + /** + * The old point of the character + */ + private final Point3D point; + + /** + * Creates a new instance + * + * @param character + * the character + * @param point + * the character point before moving + */ + public CharacterStartMovingEvent(L2Character character, Point3D point) { + this.character = character; + this.point = point; + } + + /** + * @return the old point + */ + public Point3D getPoint() { + return point; + } + + @Override + public Player getPlayer() { + return character; + } + + @Override + public Actor getActor() { + return character; + } + + @Override + public WorldObject getObject() { + return character; + } + + @Override + public L2Character getCharacter() { + return character; + } + + @Override + public ObjectID[] getDispatchableObjects() { + return new ObjectID[] { character.getID() }; + } +} diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/character/CharacterServiceImpl.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/character/CharacterServiceImpl.java index 2544be29d..aa5eeb2e4 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/character/CharacterServiceImpl.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/character/CharacterServiceImpl.java @@ -21,22 +21,12 @@ import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; import com.google.inject.Inject; -import com.l2jserver.game.net.Lineage2Client; -import com.l2jserver.game.net.SystemMessage; -import com.l2jserver.game.net.packet.server.SM_CHAR_INFO; -import com.l2jserver.game.net.packet.server.SM_CHAR_INFO_EXTRA; -import com.l2jserver.game.net.packet.server.SM_CHAR_INVENTORY; -import com.l2jserver.game.net.packet.server.SM_ACTOR_CHAT; -import com.l2jserver.game.net.packet.server.SM_ACTOR_MOVE; -import com.l2jserver.game.net.packet.server.SM_MOVE_TYPE; -import com.l2jserver.game.net.packet.server.SM_TARGET; import com.l2jserver.model.dao.CharacterDAO; import com.l2jserver.model.dao.ItemDAO; import com.l2jserver.model.id.object.CharacterID; import com.l2jserver.model.id.object.provider.CharacterIDProvider; import com.l2jserver.model.id.template.CharacterTemplateID; import com.l2jserver.model.id.template.provider.CharacterTemplateIDProvider; -import com.l2jserver.model.server.ChatMessage; import com.l2jserver.model.template.actor.ActorSex; import com.l2jserver.model.template.character.CharacterClass; import com.l2jserver.model.template.character.CharacterTemplate; @@ -49,21 +39,16 @@ import com.l2jserver.model.world.character.CharacterAppearance.CharacterFace; import com.l2jserver.model.world.character.CharacterAppearance.CharacterHairColor; import com.l2jserver.model.world.character.CharacterAppearance.CharacterHairStyle; import com.l2jserver.model.world.character.event.CharacterEnterWorldEvent; -import com.l2jserver.model.world.character.event.CharacterEvent; import com.l2jserver.model.world.character.event.CharacterLeaveWorldEvent; -import com.l2jserver.model.world.character.event.CharacterListener; import com.l2jserver.model.world.character.event.CharacterMoveEvent; import com.l2jserver.model.world.character.event.CharacterRunningEvent; +import com.l2jserver.model.world.character.event.CharacterStartMovingEvent; import com.l2jserver.model.world.character.event.CharacterTargetDeselectedEvent; import com.l2jserver.model.world.character.event.CharacterTargetSelectedEvent; import com.l2jserver.model.world.character.event.CharacterWalkingEvent; import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService.Depends; import com.l2jserver.service.game.AttackService; -import com.l2jserver.service.game.chat.ChatChannel; -import com.l2jserver.service.game.chat.ChatChannelListener; -import com.l2jserver.service.game.chat.ChatMessageType; -import com.l2jserver.service.game.chat.ChatService; import com.l2jserver.service.game.npc.NPCService; import com.l2jserver.service.game.npc.NotAttackableNPCServiceException; import com.l2jserver.service.game.spawn.AlreadySpawnedServiceException; @@ -72,7 +57,6 @@ import com.l2jserver.service.game.spawn.SpawnPointNotFoundServiceException; import com.l2jserver.service.game.spawn.SpawnService; import com.l2jserver.service.game.world.WorldService; import com.l2jserver.service.game.world.event.WorldEventDispatcher; -import com.l2jserver.service.network.NetworkService; import com.l2jserver.service.network.broadcast.BroadcastService; import com.l2jserver.service.network.gameguard.GameGuardService; import com.l2jserver.util.geometry.Coordinate; @@ -83,9 +67,8 @@ import com.l2jserver.util.geometry.Point3D; * * @author Rogiel */ -@Depends({ WorldService.class, ChatService.class, NetworkService.class, - SpawnService.class, AttackService.class, GameGuardService.class, - BroadcastService.class }) +@Depends({ WorldService.class, SpawnService.class, AttackService.class, + GameGuardService.class, BroadcastService.class }) public class CharacterServiceImpl extends AbstractService implements CharacterService { /** @@ -102,14 +85,6 @@ public class CharacterServiceImpl extends AbstractService implements * The {@link WorldService} event dispatcher */ private final WorldEventDispatcher eventDispatcher; - /** - * The {@link ChatService} - */ - private final ChatService chatService; - /** - * The {@link NetworkService} - */ - private final NetworkService networkService; /** * The {@link SpawnService} */ @@ -151,10 +126,6 @@ public class CharacterServiceImpl extends AbstractService implements * the broadcast service * @param eventDispatcher * the world service event dispatcher - * @param chatService - * the chat service - * @param networkService - * the network service * @param spawnService * the spawn service * @param npcService @@ -172,16 +143,13 @@ public class CharacterServiceImpl extends AbstractService implements */ @Inject public CharacterServiceImpl(BroadcastService broadcastService, - WorldEventDispatcher eventDispatcher, ChatService chatService, - NetworkService networkService, SpawnService spawnService, + WorldEventDispatcher eventDispatcher, SpawnService spawnService, NPCService npcService, GameGuardService ggService, CharacterDAO characterDao, ItemDAO itemDao, CharacterTemplateIDProvider charTemplateIdProvider, CharacterIDProvider charIdProvider) { this.broadcastService = broadcastService; this.eventDispatcher = eventDispatcher; - this.chatService = chatService; - this.networkService = networkService; this.spawnService = spawnService; this.npcService = npcService; this.ggService = ggService; @@ -255,85 +223,16 @@ public class CharacterServiceImpl extends AbstractService implements log.debug("Character {} is entering world", character); - final CharacterID id = character.getID(); - final Lineage2Client conn = networkService.discover(id); - if (conn == null) { - log.debug( - "Character {} cannot enter world, no Lineage2Client object found", - character); - return; - } - itemDao.loadInventory(character); - character.setOnline(true); - // inventory interfere on calculators character.getStats().updateCalculator(); - // chat listener - final ChatChannelListener globalChatListener = new ChatChannelListener() { - @Override - public void onMessage(ChatChannel channel, ChatMessage message) { - conn.write(new SM_ACTOR_CHAT(message.getSender().getObject(), - ChatMessageType.ALL, message.getMessage())); - } - }; - final ChatChannelListener tradeChatListener = new ChatChannelListener() { - @Override - public void onMessage(ChatChannel channel, ChatMessage message) { - conn.write(new SM_ACTOR_CHAT(message.getSender().getObject(), - ChatMessageType.TRADE, message.getMessage())); - } - }; - - // leave world event - eventDispatcher.addListener(id, new CharacterListener() { - @Override - protected boolean dispatch(CharacterEvent e) { - if (!(e instanceof CharacterLeaveWorldEvent)) - return true; - - log.debug( - "Character {} is leaving world, removing chat listeners", - character); - - // remove chat listeners - chatService.getGlobalChannel().removeMessageListener( - globalChatListener); - chatService.getTradeChannel().removeMessageListener( - tradeChatListener); - - // // remove broadcast listener - // eventDispatcher.removeListener(neighboorListener); - // eventDispatcher.removeListener(id, sendPacketListener);s - - // we can kill this listener now - return false; - } - }); - - // register global chat listener - chatService.getGlobalChannel().addMessageListener(globalChatListener); - chatService.getTradeChannel().addMessageListener(tradeChatListener); - // query client game guard -- if key is invalid, the connection will be // closed as soon as possible - ggService.query(conn); - - // send this user information - log.debug("Sending character information packets"); - conn.write(new SM_CHAR_INFO(character)); - conn.write(new SM_CHAR_INFO_EXTRA(character)); - conn.write(new SM_CHAR_INVENTORY(character.getInventory())); - - log.debug("Sending greeting message to client"); - conn.sendSystemMessage(SystemMessage.WELCOME_TO_LINEAGE); - conn.sendMessage("This an an development version for l2jserver 2.0"); - conn.sendMessage("Please note that many of the features are not yet implemented."); - + ggService.query(character); // start broadcasting -- will broadcast all nearby objects - broadcastService.broadcast(conn); + broadcastService.broadcast(character); // characters start in run mode try { @@ -342,14 +241,15 @@ public class CharacterServiceImpl extends AbstractService implements // we can ignore this one } - // broadcast(conn, character); - // spawn the player -- this will also dispatch a spawn event // here the object is registered in the world spawnService.spawn(character, null); // dispatch enter world event eventDispatcher.dispatch(new CharacterEnterWorldEvent(character)); + + // update character + characterDao.save(character); } @Override @@ -362,6 +262,8 @@ public class CharacterServiceImpl extends AbstractService implements spawnService.unspawn(character); eventDispatcher.dispatch(new CharacterLeaveWorldEvent(character)); character.setOnline(false); + + characterDao.save(character); } @Override @@ -372,9 +274,6 @@ public class CharacterServiceImpl extends AbstractService implements log.debug("Setting {} target to {}", character, target); - final CharacterID id = character.getID(); - final Lineage2Client conn = networkService.discover(id); - if (target == null && character.getTargetID() != null) { // if is trying to select null (remove target) and the character has // an target, trigger an deselect @@ -382,7 +281,6 @@ public class CharacterServiceImpl extends AbstractService implements character.setTargetID(null); eventDispatcher.dispatch(new CharacterTargetDeselectedEvent( character, oldTarget)); - // TODO we need to send an packet here to inform of deselection } else if (target != null && !target.getID().equals(character.getID())) { // if new target is not null and the current character target is // null or different, trigger the selection. @@ -395,8 +293,6 @@ public class CharacterServiceImpl extends AbstractService implements character.setTargetID(target.getID()); eventDispatcher.dispatch(new CharacterTargetSelectedEvent( character, target)); - conn.write(new SM_TARGET(target, character.getLevel() - - target.getLevel())); } else { // this indicates an inconsistency: reset target and throws an // exception @@ -416,8 +312,6 @@ public class CharacterServiceImpl extends AbstractService implements log.debug("Character {} is trying to attack {}", character, target); - final CharacterID id = character.getID(); - final Lineage2Client conn = networkService.discover(id); // check if this Actor can be attacked if (target instanceof NPC) { final NPC npc = (NPC) target; @@ -431,11 +325,11 @@ public class CharacterServiceImpl extends AbstractService implements // now attack the npc log.debug("Sending {} attack request to NPCService", character); - npcService.attack(npc, conn, character); + npcService.attack(npc, character); } else { - // TODO throw an exception - conn.sendActionFailed(); + throw new ActorIsNotAttackableServiceException(); } + characterDao.save(character); } @Override @@ -463,20 +357,17 @@ public class CharacterServiceImpl extends AbstractService implements log.debug("{} is moving to {}", character, coordinate); - final CharacterID id = character.getID(); - final Lineage2Client conn = networkService.discover(id); // we don't set the character coordinate here, this will be done by // validation packets, sent by client character.setState(ActorState.MOVING); character.setTargetLocation(coordinate.toPoint()); - // for now, let's just write the packet, we don't have much validation - // to be done yet. With character validation packet, another packet of - // these will be broadcasted. - conn.write(new SM_ACTOR_MOVE(character, coordinate)); - // we don't dispatch events here, they will be dispatched by - // with the same packet referred up here. + // dispatch the start moving event. BroadcastService will catch it and + // notify the client. + eventDispatcher.dispatch(new CharacterStartMovingEvent(character, + coordinate.toPoint())); + characterDao.save(character); } @Override @@ -519,8 +410,6 @@ public class CharacterServiceImpl extends AbstractService implements public void walk(L2Character character) throws CharacterAlreadyWalkingServiceException { Preconditions.checkNotNull(character, "character"); - final CharacterID id = character.getID(); - final Lineage2Client conn = networkService.discover(id); // test if character is running if (character.getMoveType() == CharacterMoveType.WALK) throw new CharacterAlreadyWalkingServiceException(); @@ -531,15 +420,12 @@ public class CharacterServiceImpl extends AbstractService implements character.setMoveType(CharacterMoveType.WALK); eventDispatcher.dispatch(new CharacterWalkingEvent(character)); - conn.write(new SM_MOVE_TYPE(character)); } @Override public void run(L2Character character) throws CharacterAlreadyRunningServiceException { Preconditions.checkNotNull(character, "character"); - final CharacterID id = character.getID(); - final Lineage2Client conn = networkService.discover(id); // test if character is walking if (character.getMoveType() == CharacterMoveType.RUN) throw new CharacterAlreadyRunningServiceException(); @@ -550,6 +436,5 @@ public class CharacterServiceImpl extends AbstractService implements character.setMoveType(CharacterMoveType.RUN); eventDispatcher.dispatch(new CharacterRunningEvent(character)); - conn.write(new SM_MOVE_TYPE(character)); } } diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/npc/NPCService.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/npc/NPCService.java index dbd36e853..7ce845702 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/npc/NPCService.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/npc/NPCService.java @@ -16,7 +16,6 @@ */ package com.l2jserver.service.game.npc; -import com.l2jserver.game.net.Lineage2Client; import com.l2jserver.game.net.packet.client.CM_CHAR_ACTION.CharacterAction; import com.l2jserver.model.template.npc.NPCTemplate; import com.l2jserver.model.world.Actor; @@ -97,13 +96,11 @@ public interface NPCService extends Service { * * @param npc * the npc - * @param conn - * the {@link Lineage2Client} object * @param attacker * the character * @throws NotAttackableNPCServiceException * if {@link NPC} is not attackable */ - void attack(NPC npc, Lineage2Client conn, L2Character attacker) + void attack(NPC npc, L2Character attacker) throws NotAttackableNPCServiceException; } diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/npc/NPCServiceImpl.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/npc/NPCServiceImpl.java index 113854a08..1b3ceebd3 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/npc/NPCServiceImpl.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/npc/NPCServiceImpl.java @@ -255,10 +255,9 @@ public class NPCServiceImpl extends AbstractService implements NPCService { } @Override - public void attack(NPC npc, Lineage2Client conn, L2Character attacker) + public void attack(NPC npc, L2Character attacker) throws NotAttackableNPCServiceException { Preconditions.checkNotNull(npc, "npc"); - Preconditions.checkNotNull(conn, "conn"); Preconditions.checkNotNull(attacker, "attacker"); log.debug("{} is being attacked by {}", npc, attacker); diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/world/event/WorldEventDispatcherImpl.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/world/event/WorldEventDispatcherImpl.java index 6076759f0..35fc829f7 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/world/event/WorldEventDispatcherImpl.java +++ b/l2jserver2-gameserver/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.TimerTask; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -83,44 +82,53 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher { } /** - * Stats the world event disptacher + * Stats the world event dispatcher */ public void start() { - threadPool = threadService.createThreadPool("event-dispatcher", 1); - threadPool.async(0, TimeUnit.MILLISECONDS, 20, new TimerTask() { - @Override - public void run() { - EventContainer event; - while ((event = events.poll()) != null) { - try { - log.debug("Dispatching event {}", event.event); + final int threads = Runtime.getRuntime().availableProcessors(); + threadPool = threadService + .createThreadPool("event-dispatcher", threads); + for (int i = 0; i < threads; i++) { + threadPool.async(0, TimeUnit.MILLISECONDS, 10, new Runnable() { + @Override + public void run() { + EventContainer event; + while ((event = events.poll()) != null) { + synchronized (event) { + try { + if (event.future.isCancelled()) + continue; - // set state - event.future.running = true; - event.future.complete = false; + log.debug("Dispatching event {}", event.event); - // dispatch - if (doDispatch(event)) - // the set will update state - event.future.set(event.event); - } catch (Throwable t) { - event.future.setException(t); - log.warn("Exception in WorldEventDispatcher thread", t); + // set state + event.future.running = true; + event.future.complete = false; + + // dispatch + doDispatch(event.event); + // the set will update state + event.future.set(event.event); + } catch (Throwable t) { + event.future.setException(t); + log.warn( + "Exception in WorldEventDispatcher thread", + t); + } + } } } - } - }); + }); + } } @Override - public WorldEventFuture dispatch(E event) { + public WorldEventFuture dispatch(final E event) { Preconditions.checkNotNull(event, "event"); log.debug("Queing dispatch for event {}", event); + final WorldEventFutureImpl future = new WorldEventFutureImpl(); events.add(new EventContainer(event, future)); - // final WorldEventFutureImpl future = new WorldEventFutureImpl(); - // final EventContainer c = new EventContainer(event, future); - // doDispatch(c); return future; } @@ -129,18 +137,15 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher { * * @param event * the event - * @return true if dispatch was not canceled */ - public synchronized boolean doDispatch(EventContainer event) { - final ObjectID[] objects = event.event.getDispatchableObjects(); + private synchronized void doDispatch(WorldEvent event) { + final ObjectID[] objects = event.getDispatchableObjects(); for (ObjectID obj : objects) { if (obj == null) continue; for (final WorldListener listener : globalListeners) { - if (event.future.isCancelled()) - return false; try { - if (!listener.dispatch(event.event)) + if (!listener.dispatch(event)) // remove listener if return value is false globalListeners.remove(listener); } catch (Throwable t) { @@ -151,10 +156,8 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher { } final Set listeners = getListeners(obj); for (final WorldListener listener : listeners) { - if (event.future.isCancelled()) - return false; try { - if (!listener.dispatch(event.event)) + if (!listener.dispatch(event)) // remove listener if return value is false listeners.remove(listener); } catch (Throwable t) { @@ -164,7 +167,6 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher { } } } - return true; } @Override @@ -288,10 +290,10 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher { } @Override - public void await() throws InterruptedException { + public void await() throws ExecutionException { try { super.get(); - } catch (ExecutionException e) { + } catch (InterruptedException e) { } } diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/world/event/WorldEventFuture.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/world/event/WorldEventFuture.java index 772a9baf2..a4e76b182 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/world/event/WorldEventFuture.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/world/event/WorldEventFuture.java @@ -16,6 +16,7 @@ */ package com.l2jserver.service.game.world.event; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -34,10 +35,10 @@ public interface WorldEventFuture extends Future { /** * Waits until the event is dispatched to all listeners * - * @throws InterruptedException - * if the thread has been interrupted while waiting + * @throws ExecutionException + * if any error occur while dispatching the event */ - void await() throws InterruptedException; + void await() throws ExecutionException; /** * Waits until the event is dispatched to all listeners diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/NettyNetworkService.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/NettyNetworkService.java index 1368917b6..140443f55 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/NettyNetworkService.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/NettyNetworkService.java @@ -17,7 +17,7 @@ package com.l2jserver.service.network; import java.util.Set; -import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelFuture; @@ -26,6 +26,8 @@ import org.jboss.netty.channel.ServerChannel; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; import org.jboss.netty.logging.InternalLoggerFactory; import org.jboss.netty.logging.Slf4JLoggerFactory; +import org.jboss.netty.util.ThreadNameDeterminer; +import org.jboss.netty.util.ThreadRenamingRunnable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,8 +43,12 @@ import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService.Depends; import com.l2jserver.service.configuration.ConfigurationService; import com.l2jserver.service.core.LoggingService; +import com.l2jserver.service.core.threading.ThreadPool; +import com.l2jserver.service.core.threading.ThreadPoolPriority; +import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.service.game.world.WorldService; import com.l2jserver.service.network.keygen.BlowfishKeygenService; +import com.l2jserver.util.ThreadPoolUtils; import com.l2jserver.util.factory.CollectionFactory; /** @@ -50,8 +56,8 @@ import com.l2jserver.util.factory.CollectionFactory; * * @author Rogiel */ -@Depends({ LoggingService.class, BlowfishKeygenService.class, - WorldService.class }) +@Depends({ LoggingService.class, ThreadService.class, + BlowfishKeygenService.class, WorldService.class }) public class NettyNetworkService extends AbstractService implements NetworkService { /** @@ -59,6 +65,11 @@ public class NettyNetworkService extends AbstractService implements */ private final Logger log = LoggerFactory.getLogger(this.getClass()); + /** + * The {@link ThreadService} + */ + private final ThreadService threadService; + /** * The network configuration object */ @@ -68,6 +79,14 @@ public class NettyNetworkService extends AbstractService implements */ private final Injector injector; + /** + * Netty Boss {@link ThreadPool} + */ + private ThreadPool bossPool; + /** + * Netty Worker {@link ThreadPool} + */ + private ThreadPool workerPool; /** * The server bootstrap */ @@ -86,10 +105,13 @@ public class NettyNetworkService extends AbstractService implements * the configuration service * @param injector * the {@link Guice} {@link Injector} + * @param threadService + * the {@link ThreadService} */ @Inject public NettyNetworkService(ConfigurationService configService, - Injector injector) { + Injector injector, ThreadService threadService) { + this.threadService = threadService; this.config = configService.get(NetworkConfiguration.class); this.injector = injector; InternalLoggerFactory.setDefaultFactory(new Slf4JLoggerFactory()); @@ -97,9 +119,24 @@ public class NettyNetworkService extends AbstractService implements @Override protected void doStart() { + bossPool = threadService.createThreadPool("netty-boss", 10, 60, + TimeUnit.SECONDS, ThreadPoolPriority.HIGH); + workerPool = threadService.createThreadPool("netty-worker", 50, 60, + TimeUnit.SECONDS, ThreadPoolPriority.HIGH); + + ThreadRenamingRunnable + .setThreadNameDeterminer(new ThreadNameDeterminer() { + @Override + public String determineThreadName(String currentThreadName, + String proposedThreadName) throws Exception { + return currentThreadName; + } + }); + server = new ServerBootstrap(new NioServerSocketChannelFactory( - Executors.newCachedThreadPool(), - Executors.newCachedThreadPool())); + ThreadPoolUtils.wrap(bossPool), + ThreadPoolUtils.wrap(workerPool), 50)); + server.setPipelineFactory(new Lineage2PipelineFactory(injector, this)); channel = (ServerChannel) server.bind(config.getListenAddress()); } @@ -160,9 +197,15 @@ public class NettyNetworkService extends AbstractService implements protected void doStop() { try { channel.close().awaitUninterruptibly(); + server.releaseExternalResources(); + bossPool.dispose(); + workerPool.dispose(); } finally { server = null; channel = null; + bossPool = null; + workerPool = null; } + clients.clear(); } } diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/broadcast/BroadcastService.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/broadcast/BroadcastService.java index d75057ba1..9b71c7068 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/broadcast/BroadcastService.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/broadcast/BroadcastService.java @@ -16,7 +16,7 @@ */ package com.l2jserver.service.network.broadcast; -import com.l2jserver.game.net.Lineage2Client; +import com.l2jserver.model.world.L2Character; import com.l2jserver.model.world.WorldObject; import com.l2jserver.service.Service; @@ -31,7 +31,7 @@ public interface BroadcastService extends Service { * Broadcast all nearby objects to the given client * * @param conn - * the Lineage 2 client + * the character */ - void broadcast(Lineage2Client conn); + void broadcast(L2Character conn); } diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/broadcast/BroadcastServiceImpl.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/broadcast/BroadcastServiceImpl.java index a969376fb..cfcb2e050 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/broadcast/BroadcastServiceImpl.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/broadcast/BroadcastServiceImpl.java @@ -23,16 +23,22 @@ import com.google.common.base.Preconditions; import com.google.inject.Inject; import com.l2jserver.game.net.Lineage2Client; import com.l2jserver.game.net.SystemMessage; -import com.l2jserver.game.net.packet.server.SM_ATTACK; -import com.l2jserver.game.net.packet.server.SM_CHAR_INFO_BROADCAST; +import com.l2jserver.game.net.packet.server.SM_ACTOR_CHAT; import com.l2jserver.game.net.packet.server.SM_ACTOR_DIE; +import com.l2jserver.game.net.packet.server.SM_ACTOR_MOVE; +import com.l2jserver.game.net.packet.server.SM_ATTACK; +import com.l2jserver.game.net.packet.server.SM_CHAR_INFO; +import com.l2jserver.game.net.packet.server.SM_CHAR_INFO_BROADCAST; +import com.l2jserver.game.net.packet.server.SM_CHAR_INFO_EXTRA; +import com.l2jserver.game.net.packet.server.SM_CHAR_INVENTORY; import com.l2jserver.game.net.packet.server.SM_ITEM_GROUND; import com.l2jserver.game.net.packet.server.SM_ITEM_PICK; -import com.l2jserver.game.net.packet.server.SM_ACTOR_MOVE; import com.l2jserver.game.net.packet.server.SM_MOVE_TYPE; import com.l2jserver.game.net.packet.server.SM_NPC_INFO; import com.l2jserver.game.net.packet.server.SM_OBJECT_REMOVE; +import com.l2jserver.game.net.packet.server.SM_TARGET; import com.l2jserver.model.id.object.CharacterID; +import com.l2jserver.model.server.ChatMessage; import com.l2jserver.model.world.Item; import com.l2jserver.model.world.L2Character; import com.l2jserver.model.world.NPC; @@ -43,9 +49,13 @@ import com.l2jserver.model.world.actor.event.ActorDieEvent; import com.l2jserver.model.world.actor.event.ActorTeleportingEvent; import com.l2jserver.model.world.actor.event.ActorUnspawnEvent; import com.l2jserver.model.world.character.event.CharacterEnterWorldEvent; +import com.l2jserver.model.world.character.event.CharacterEvent; import com.l2jserver.model.world.character.event.CharacterLeaveWorldEvent; +import com.l2jserver.model.world.character.event.CharacterListener; import com.l2jserver.model.world.character.event.CharacterMoveEvent; import com.l2jserver.model.world.character.event.CharacterRunningEvent; +import com.l2jserver.model.world.character.event.CharacterStartMovingEvent; +import com.l2jserver.model.world.character.event.CharacterTargetSelectedEvent; import com.l2jserver.model.world.character.event.CharacterWalkingEvent; import com.l2jserver.model.world.item.ItemDropEvent; import com.l2jserver.model.world.item.ItemPickEvent; @@ -53,6 +63,10 @@ import com.l2jserver.model.world.npc.event.NPCSpawnEvent; import com.l2jserver.model.world.player.event.PlayerTeleportedEvent; import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService.Depends; +import com.l2jserver.service.game.chat.ChatChannel; +import com.l2jserver.service.game.chat.ChatChannelListener; +import com.l2jserver.service.game.chat.ChatMessageType; +import com.l2jserver.service.game.chat.ChatService; import com.l2jserver.service.game.world.WorldService; import com.l2jserver.service.game.world.event.FilteredWorldListener; import com.l2jserver.service.game.world.event.WorldEvent; @@ -78,6 +92,15 @@ public class BroadcastServiceImpl extends AbstractService implements * The world service */ private final WorldService worldService; + /** + * The {@link ChatService} + */ + private final ChatService chatService; + /** + * The {@link ChatService} + */ + private final NetworkService networkService; + /** * The world service event dispatcher */ @@ -86,21 +109,28 @@ public class BroadcastServiceImpl extends AbstractService implements /** * @param worldService * the world service + * @param chatService + * the chat service + * @param networkService + * the network service * @param eventDispatcher * the world service event disptacher */ @Inject public BroadcastServiceImpl(WorldService worldService, + ChatService chatService, NetworkService networkService, WorldEventDispatcher eventDispatcher) { this.worldService = worldService; + this.chatService = chatService; + this.networkService = networkService; this.eventDispatcher = eventDispatcher; } @Override - public void broadcast(final Lineage2Client conn) { - Preconditions.checkNotNull(conn, "conn"); - final L2Character character = conn.getCharacter(); + public void broadcast(final L2Character character) { Preconditions.checkNotNull(character, "character"); + final Lineage2Client conn = networkService.discover(character.getID()); + Preconditions.checkNotNull(conn, "conn"); final CharacterID id = character.getID(); log.debug("Starting character broadcast"); @@ -121,8 +151,8 @@ public class BroadcastServiceImpl extends AbstractService implements broadcast(conn, e.getObject()); } else if (e instanceof CharacterMoveEvent) { final CharacterMoveEvent evt = (CharacterMoveEvent) e; - conn.write(new SM_ACTOR_MOVE((L2Character) object, evt.getPoint() - .getCoordinate())); + conn.write(new SM_ACTOR_MOVE((L2Character) object, evt + .getPoint().getCoordinate())); } else if (e instanceof PlayerTeleportedEvent || e instanceof CharacterEnterWorldEvent) { broadcast(conn, e.getObject()); @@ -134,7 +164,6 @@ public class BroadcastServiceImpl extends AbstractService implements || e instanceof CharacterLeaveWorldEvent || e instanceof ActorUnspawnEvent) { // object is now out of sight - // FIXME pick up animation is not happening conn.write(new SM_OBJECT_REMOVE(object)); } else if (e instanceof CharacterWalkingEvent) { conn.write(new SM_MOVE_TYPE(((CharacterWalkingEvent) e) @@ -160,15 +189,93 @@ public class BroadcastServiceImpl extends AbstractService implements if (e instanceof CharacterMoveEvent) { final CharacterMoveEvent evt = (CharacterMoveEvent) e; // process update known list - broadcastUpdate(conn, character, evt.getPoint()); - } else if (e instanceof PlayerTeleportedEvent - || e instanceof CharacterEnterWorldEvent) { + broadcastUpdate(conn, evt.getCharacter(), evt.getPoint()); + } else if (e instanceof CharacterEnterWorldEvent) { + final CharacterEnterWorldEvent evt = (CharacterEnterWorldEvent) e; + final L2Character character = evt.getCharacter(); + final CharacterID id = character.getID(); + + // chat listener + final ChatChannelListener globalChatListener = new ChatChannelListener() { + @Override + public void onMessage(ChatChannel channel, + ChatMessage message) { + conn.write(new SM_ACTOR_CHAT(message.getSender() + .getObject(), ChatMessageType.ALL, message + .getMessage())); + } + }; + final ChatChannelListener tradeChatListener = new ChatChannelListener() { + @Override + public void onMessage(ChatChannel channel, + ChatMessage message) { + conn.write(new SM_ACTOR_CHAT(message.getSender() + .getObject(), ChatMessageType.TRADE, + message.getMessage())); + } + }; + + // leave world event + eventDispatcher.addListener(id, new CharacterListener() { + @Override + protected boolean dispatch(CharacterEvent e) { + if (!(e instanceof CharacterLeaveWorldEvent)) + return true; + + log.debug( + "Character {} is leaving world, removing chat listeners", + character); + + // remove chat listeners + chatService.getGlobalChannel() + .removeMessageListener(globalChatListener); + chatService.getTradeChannel() + .removeMessageListener(tradeChatListener); + + // we can kill this listener now + return false; + } + }); + + // register global chat listener + chatService.getGlobalChannel().addMessageListener( + globalChatListener); + chatService.getTradeChannel().addMessageListener( + tradeChatListener); + + log.debug("Sending greeting message to client"); + conn.sendSystemMessage(SystemMessage.WELCOME_TO_LINEAGE); + conn.sendMessage("This an an development version for l2jserver 2.0"); + conn.sendMessage("Please note that many of the features are not yet implemented."); + + // send this user information + log.debug("Sending character information packets"); + conn.write(new SM_CHAR_INFO(evt.getCharacter())); + conn.write(new SM_CHAR_INFO_EXTRA(evt.getCharacter())); + conn.write(new SM_CHAR_INVENTORY(evt.getCharacter() + .getInventory())); + + broadcastAll(conn, character); + } else if (e instanceof CharacterStartMovingEvent) { + conn.write(new SM_ACTOR_MOVE( + ((CharacterStartMovingEvent) e).getCharacter(), + ((CharacterStartMovingEvent) e).getPoint() + .getCoordinate())); + } else if (e instanceof CharacterTargetSelectedEvent) { + final CharacterTargetSelectedEvent evt = (CharacterTargetSelectedEvent) e; + conn.write(new SM_TARGET(evt.getTarget(), evt + .getCharacter().getLevel() + - evt.getTarget().getLevel())); + } else if (e instanceof PlayerTeleportedEvent) { broadcastAll(conn, character); } else if (e instanceof ActorAttackHitEvent) { conn.write(new SM_ATTACK(((ActorAttackHitEvent) e).getHit())); conn.sendSystemMessage(SystemMessage.YOU_DID_S1_DMG, (int) ((ActorAttackHitEvent) e).getHit() .getDamage()); + } else if (e instanceof CharacterWalkingEvent + || e instanceof CharacterRunningEvent) { + conn.write(new SM_MOVE_TYPE((L2Character) e.getObject())); } // keep listener alive return true; diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/gameguard/GameGuardService.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/gameguard/GameGuardService.java index 5a4aa38af..0837ea1f5 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/gameguard/GameGuardService.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/gameguard/GameGuardService.java @@ -19,6 +19,7 @@ package com.l2jserver.service.network.gameguard; import java.util.concurrent.Future; import com.l2jserver.game.net.Lineage2Client; +import com.l2jserver.model.world.L2Character; import com.l2jserver.service.Service; /** @@ -30,11 +31,11 @@ public interface GameGuardService extends Service { /** * Queries the client GameGuard for an response * - * @param conn - * the lineage 2 connection + * @param character + * the lineage 2 character * @return an future that will be used to obtain validation status */ - Future query(Lineage2Client conn); + Future query(L2Character character); /** * The Game guard key state diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/gameguard/GameGuardServiceImpl.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/gameguard/GameGuardServiceImpl.java index faa527499..f27f4721a 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/gameguard/GameGuardServiceImpl.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/gameguard/GameGuardServiceImpl.java @@ -27,12 +27,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.util.concurrent.AbstractFuture; +import com.google.inject.Inject; import com.l2jserver.game.net.Lineage2Client; import com.l2jserver.game.net.packet.server.SM_GG_QUERY; +import com.l2jserver.model.world.L2Character; import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService.Depends; import com.l2jserver.service.ServiceStartException; import com.l2jserver.service.ServiceStopException; +import com.l2jserver.service.game.chat.ChatService; import com.l2jserver.service.network.NetworkService; import com.l2jserver.util.factory.CollectionFactory; @@ -64,6 +67,11 @@ public class GameGuardServiceImpl extends AbstractService implements (byte) 0xde, (byte) 0xc3, 0x68, (byte) 0xf6, 0x2d, 0x23, (byte) 0xf1, 0x3f, (byte) 0xee, 0x68, 0x5b, (byte) 0xc5 }; + /** + * The {@link ChatService} + */ + private final NetworkService networkService; + /** * The map containing all pending futures */ @@ -76,6 +84,15 @@ public class GameGuardServiceImpl extends AbstractService implements @SuppressWarnings("unused") private MessageDigest digester; + /** + * @param networkService + * the network service + */ + @Inject + private GameGuardServiceImpl(NetworkService networkService) { + this.networkService = networkService; + } + @Override protected void doStart() throws ServiceStartException { futures = CollectionFactory.newMap(); @@ -87,8 +104,9 @@ public class GameGuardServiceImpl extends AbstractService implements } @Override - public Future query(final Lineage2Client conn) { + public Future query(final L2Character character) { log.debug("Quering client for GameGuard authentication key"); + final Lineage2Client conn = networkService.discover(character.getID()); conn.write(new SM_GG_QUERY(STATIC_KEY)).addListener( new ChannelFutureListener() { @Override