1
0
mirror of https://github.com/Rogiel/l2jserver2 synced 2025-12-06 07:32:46 +00:00

Decouples CharacterService from NetworkService

This commit is contained in:
2011-12-18 18:07:59 -02:00
parent 87a1c0d3a5
commit b9e460b70c
17 changed files with 702 additions and 244 deletions

View File

@@ -19,8 +19,7 @@ package com.l2jserver.service.core.threading;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* This is an ThreadPool you can use it to schedule tasks in the future or to * This is an ThreadPool that you can use to asynchronously execute tasks.
* repeat them many times.
* *
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
*/ */
@@ -72,4 +71,9 @@ public interface ThreadPool {
* execute tasks. * execute tasks.
*/ */
void dispose(); void dispose();
/**
* @return true if the thread pool is no longer usable (i.e. was disposed)
*/
boolean isDisposed();
} }

View File

@@ -0,0 +1,55 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.core.threading;
/**
* The priority of the thread pool
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public enum ThreadPoolPriority {
/**
* High priority.
* <p>
* 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.
* <p>
* Processor will block {@link ThreadPoolPriority#LOW} priority threads in
* order to finish tasks in pools on this priority.
*/
NORMAL(Thread.NORM_PRIORITY),
/**
* Low priority.
* <p>
* 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;
}
}

View File

@@ -79,16 +79,72 @@ public interface ThreadService extends Service {
ScheduledAsyncFuture async(long delay, TimeUnit unit, long repeat, ScheduledAsyncFuture async(long delay, TimeUnit unit, long repeat,
Runnable task); 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.
* <p>
* 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.
* <p>
* 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 <code>threadTimeout</code>
* @return the new thread pool
*/
ThreadPool createThreadPool(String name, int threads, long threadTimeout,
TimeUnit threadTimeoutUnit);
/** /**
* Creates a new thread pool. * Creates a new thread pool.
* *
* @param name * @param name
* the pool name * the pool name
* @param maxThreads * @param threads
* the maximum amount of threads. * the maximum amount of active threads
* @param threadTimeout
* the time it takes to expire an inactive thread
* @param threadTimeoutUnit
* the {@link TimeUnit} for <code>threadTimeout</code>
* @param priority
* the processor scheduling priority
* @return the new thread pool * @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 * Disposes an given thread pool. After disposing the thread pool will no

View File

@@ -22,12 +22,13 @@ import java.util.Map.Entry;
import java.util.concurrent.Delayed; import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -62,7 +63,7 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService
@Override @Override
protected void doStart() throws ServiceStartException { protected void doStart() throws ServiceStartException {
threadPools = CollectionFactory.newMap(); threadPools = CollectionFactory.newMap();
pool = createThreadPool("shared", 20); pool = createThreadPool("shared", 1);
pool.async(50, TimeUnit.MILLISECONDS, 50, new Runnable() { pool.async(50, TimeUnit.MILLISECONDS, 50, new Runnable() {
@Override @Override
@@ -111,21 +112,61 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService
} }
@Override @Override
public ThreadPool createThreadPool(String name, int maxThreads) { public ThreadPool createThreadPool(final String name, final int threads,
log.debug("Creating new ThreadPool {} with maximum of {} threads", final long threadTimeout, final TimeUnit threadTimeoutUnit,
name, maxThreads); final ThreadPoolPriority priority) {
final ThreadPoolImpl pool = new ThreadPoolImpl(name, log.debug(
Executors.newScheduledThreadPool(maxThreads)); "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); threadPools.put(name, pool);
return 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 @Override
public void dispose(ThreadPool pool) { public void dispose(ThreadPool pool) {
log.debug("Disposing ThreadPool {}", pool); log.debug("Disposing ThreadPool {}", pool);
if (pool instanceof ThreadPoolImpl) { if (pool instanceof ThreadPoolImpl) {
((ThreadPoolImpl) pool).executor.shutdown(); ((ThreadPoolImpl) pool).executor.shutdown();
threadPools.remove(((ThreadPoolImpl) pool).name); threadPools.remove(((ThreadPoolImpl) pool).name);
return;
} }
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"The given ThreadPool is not supported by this service"); "The given ThreadPool is not supported by this service");
@@ -331,7 +372,7 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService
/** /**
* The backing executor * The backing executor
*/ */
private final ScheduledExecutorService executor; private final ScheduledThreadPoolExecutor executor;
/** /**
* The list of active and pending futures * The list of active and pending futures
*/ */
@@ -342,9 +383,9 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService
* @param name * @param name
* the pool name * the pool name
* @param executor * @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.name = name;
this.executor = executor; this.executor = executor;
} }
@@ -392,5 +433,10 @@ public class ThreadServiceImpl extends AbstractService implements ThreadService
} }
} }
} }
@Override
public boolean isDisposed() {
return executor.isShutdown();
}
} }
} }

View File

@@ -0,0 +1,156 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
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 <a href="http://www.rogiel.com">Rogiel</a>
*/
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<Runnable> 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 <T> Future<T> submit(Callable<T> task) {
return pool.async(wrap(task));
}
@Override
public <T> Future<T> 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 <T> List<Future<T>> invokeAll(
Collection<? extends Callable<T>> tasks)
throws InterruptedException {
throw new UnsupportedOperationException();
}
@Override
public <T> List<Future<T>> invokeAll(
Collection<? extends Callable<T>> tasks, long timeout,
TimeUnit unit) throws InterruptedException {
throw new UnsupportedOperationException();
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
throw new UnsupportedOperationException();
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> 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<Runnable> wrap(final Runnable command) {
return new Task<Runnable>() {
@Override
public Runnable call() throws Exception {
command.run();
return command;
}
};
}
/**
* Wraps an {@link Runnable} into an {@link Task}
*
* @param <T>
* the {@link Task} return type
* @param command
* the {@link Runnable} to be wrapped
* @return the wrapped {@link Runnable}
*/
public static <T> Task<T> wrap(final Callable<T> command) {
return new Task<T>() {
@Override
public T call() throws Exception {
return command.call();
}
};
}
}

View File

@@ -16,7 +16,9 @@
*/ */
package com.l2jserver; package com.l2jserver;
import com.l2jserver.service.Service;
import com.l2jserver.service.ServiceManager; import com.l2jserver.service.ServiceManager;
import com.l2jserver.service.ServiceStopException;
import com.l2jserver.service.cache.CacheService; import com.l2jserver.service.cache.CacheService;
import com.l2jserver.service.configuration.ConfigurationService; import com.l2jserver.service.configuration.ConfigurationService;
import com.l2jserver.service.database.DatabaseService; import com.l2jserver.service.database.DatabaseService;
@@ -37,41 +39,42 @@ import com.l2jserver.service.network.keygen.BlowfishKeygenService;
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
*/ */
public class L2JGameServerMain { 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 * Main method
* *
* @param args * @param args
* no arguments are used * no arguments are used
*/ */
@SuppressWarnings("unchecked")
public static void main(String[] args) { public static void main(String[] args) {
final L2JGameServer server = new L2JGameServer(); final L2JGameServer server = new L2JGameServer();
try { try {
final ServiceManager serviceManager = server.getInjector() final ServiceManager serviceManager = server.getInjector()
.getInstance(ServiceManager.class); .getInstance(ServiceManager.class);
serviceManager.start(CacheService.class); for (final Class<?> service : SERVICES) {
serviceManager.start(ConfigurationService.class); serviceManager.start((Class<? extends Service>) service);
serviceManager.start(DatabaseService.class); }
serviceManager.start(WorldIDService.class);
serviceManager.start(ScriptingService.class); Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
serviceManager.start(TemplateService.class); @Override
public void run() {
serviceManager.start(ChatService.class); for (final Class<?> service : SERVICES) {
serviceManager.start(NPCService.class); try {
serviceManager.start(ItemService.class); serviceManager
.stop((Class<? extends Service>) service);
serviceManager.start(CharacterService.class); } catch (ServiceStopException e) {
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();
} catch (Exception e) { } catch (Exception e) {
System.out.println("GameServer could not be started!"); System.out.println("GameServer could not be started!");
e.printStackTrace(); e.printStackTrace();

View File

@@ -0,0 +1,85 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
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 <a href="http://www.rogiel.com">Rogiel</a>
*/
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() };
}
}

View File

@@ -21,22 +21,12 @@ import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.inject.Inject; 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.CharacterDAO;
import com.l2jserver.model.dao.ItemDAO; import com.l2jserver.model.dao.ItemDAO;
import com.l2jserver.model.id.object.CharacterID; import com.l2jserver.model.id.object.CharacterID;
import com.l2jserver.model.id.object.provider.CharacterIDProvider; import com.l2jserver.model.id.object.provider.CharacterIDProvider;
import com.l2jserver.model.id.template.CharacterTemplateID; import com.l2jserver.model.id.template.CharacterTemplateID;
import com.l2jserver.model.id.template.provider.CharacterTemplateIDProvider; 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.actor.ActorSex;
import com.l2jserver.model.template.character.CharacterClass; import com.l2jserver.model.template.character.CharacterClass;
import com.l2jserver.model.template.character.CharacterTemplate; 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.CharacterHairColor;
import com.l2jserver.model.world.character.CharacterAppearance.CharacterHairStyle; import com.l2jserver.model.world.character.CharacterAppearance.CharacterHairStyle;
import com.l2jserver.model.world.character.event.CharacterEnterWorldEvent; 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.CharacterLeaveWorldEvent;
import com.l2jserver.model.world.character.event.CharacterListener;
import com.l2jserver.model.world.character.event.CharacterMoveEvent; import com.l2jserver.model.world.character.event.CharacterMoveEvent;
import com.l2jserver.model.world.character.event.CharacterRunningEvent; 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.CharacterTargetDeselectedEvent;
import com.l2jserver.model.world.character.event.CharacterTargetSelectedEvent; import com.l2jserver.model.world.character.event.CharacterTargetSelectedEvent;
import com.l2jserver.model.world.character.event.CharacterWalkingEvent; import com.l2jserver.model.world.character.event.CharacterWalkingEvent;
import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService;
import com.l2jserver.service.AbstractService.Depends; import com.l2jserver.service.AbstractService.Depends;
import com.l2jserver.service.game.AttackService; 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.NPCService;
import com.l2jserver.service.game.npc.NotAttackableNPCServiceException; import com.l2jserver.service.game.npc.NotAttackableNPCServiceException;
import com.l2jserver.service.game.spawn.AlreadySpawnedServiceException; 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.spawn.SpawnService;
import com.l2jserver.service.game.world.WorldService; import com.l2jserver.service.game.world.WorldService;
import com.l2jserver.service.game.world.event.WorldEventDispatcher; 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.broadcast.BroadcastService;
import com.l2jserver.service.network.gameguard.GameGuardService; import com.l2jserver.service.network.gameguard.GameGuardService;
import com.l2jserver.util.geometry.Coordinate; import com.l2jserver.util.geometry.Coordinate;
@@ -83,9 +67,8 @@ import com.l2jserver.util.geometry.Point3D;
* *
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
*/ */
@Depends({ WorldService.class, ChatService.class, NetworkService.class, @Depends({ WorldService.class, SpawnService.class, AttackService.class,
SpawnService.class, AttackService.class, GameGuardService.class, GameGuardService.class, BroadcastService.class })
BroadcastService.class })
public class CharacterServiceImpl extends AbstractService implements public class CharacterServiceImpl extends AbstractService implements
CharacterService { CharacterService {
/** /**
@@ -102,14 +85,6 @@ public class CharacterServiceImpl extends AbstractService implements
* The {@link WorldService} event dispatcher * The {@link WorldService} event dispatcher
*/ */
private final WorldEventDispatcher eventDispatcher; private final WorldEventDispatcher eventDispatcher;
/**
* The {@link ChatService}
*/
private final ChatService chatService;
/**
* The {@link NetworkService}
*/
private final NetworkService networkService;
/** /**
* The {@link SpawnService} * The {@link SpawnService}
*/ */
@@ -151,10 +126,6 @@ public class CharacterServiceImpl extends AbstractService implements
* the broadcast service * the broadcast service
* @param eventDispatcher * @param eventDispatcher
* the world service event dispatcher * the world service event dispatcher
* @param chatService
* the chat service
* @param networkService
* the network service
* @param spawnService * @param spawnService
* the spawn service * the spawn service
* @param npcService * @param npcService
@@ -172,16 +143,13 @@ public class CharacterServiceImpl extends AbstractService implements
*/ */
@Inject @Inject
public CharacterServiceImpl(BroadcastService broadcastService, public CharacterServiceImpl(BroadcastService broadcastService,
WorldEventDispatcher eventDispatcher, ChatService chatService, WorldEventDispatcher eventDispatcher, SpawnService spawnService,
NetworkService networkService, SpawnService spawnService,
NPCService npcService, GameGuardService ggService, NPCService npcService, GameGuardService ggService,
CharacterDAO characterDao, ItemDAO itemDao, CharacterDAO characterDao, ItemDAO itemDao,
CharacterTemplateIDProvider charTemplateIdProvider, CharacterTemplateIDProvider charTemplateIdProvider,
CharacterIDProvider charIdProvider) { CharacterIDProvider charIdProvider) {
this.broadcastService = broadcastService; this.broadcastService = broadcastService;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
this.chatService = chatService;
this.networkService = networkService;
this.spawnService = spawnService; this.spawnService = spawnService;
this.npcService = npcService; this.npcService = npcService;
this.ggService = ggService; this.ggService = ggService;
@@ -255,85 +223,16 @@ public class CharacterServiceImpl extends AbstractService implements
log.debug("Character {} is entering world", character); 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); itemDao.loadInventory(character);
character.setOnline(true); character.setOnline(true);
// inventory interfere on calculators // inventory interfere on calculators
character.getStats().updateCalculator(); 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 // query client game guard -- if key is invalid, the connection will be
// closed as soon as possible // closed as soon as possible
ggService.query(conn); ggService.query(character);
// 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.");
// start broadcasting -- will broadcast all nearby objects // start broadcasting -- will broadcast all nearby objects
broadcastService.broadcast(conn); broadcastService.broadcast(character);
// characters start in run mode // characters start in run mode
try { try {
@@ -342,14 +241,15 @@ public class CharacterServiceImpl extends AbstractService implements
// we can ignore this one // we can ignore this one
} }
// broadcast(conn, character);
// spawn the player -- this will also dispatch a spawn event // spawn the player -- this will also dispatch a spawn event
// here the object is registered in the world // here the object is registered in the world
spawnService.spawn(character, null); spawnService.spawn(character, null);
// dispatch enter world event // dispatch enter world event
eventDispatcher.dispatch(new CharacterEnterWorldEvent(character)); eventDispatcher.dispatch(new CharacterEnterWorldEvent(character));
// update character
characterDao.save(character);
} }
@Override @Override
@@ -362,6 +262,8 @@ public class CharacterServiceImpl extends AbstractService implements
spawnService.unspawn(character); spawnService.unspawn(character);
eventDispatcher.dispatch(new CharacterLeaveWorldEvent(character)); eventDispatcher.dispatch(new CharacterLeaveWorldEvent(character));
character.setOnline(false); character.setOnline(false);
characterDao.save(character);
} }
@Override @Override
@@ -372,9 +274,6 @@ public class CharacterServiceImpl extends AbstractService implements
log.debug("Setting {} target to {}", character, target); 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 (target == null && character.getTargetID() != null) {
// if is trying to select null (remove target) and the character has // if is trying to select null (remove target) and the character has
// an target, trigger an deselect // an target, trigger an deselect
@@ -382,7 +281,6 @@ public class CharacterServiceImpl extends AbstractService implements
character.setTargetID(null); character.setTargetID(null);
eventDispatcher.dispatch(new CharacterTargetDeselectedEvent( eventDispatcher.dispatch(new CharacterTargetDeselectedEvent(
character, oldTarget)); character, oldTarget));
// TODO we need to send an packet here to inform of deselection
} else if (target != null && !target.getID().equals(character.getID())) { } else if (target != null && !target.getID().equals(character.getID())) {
// if new target is not null and the current character target is // if new target is not null and the current character target is
// null or different, trigger the selection. // null or different, trigger the selection.
@@ -395,8 +293,6 @@ public class CharacterServiceImpl extends AbstractService implements
character.setTargetID(target.getID()); character.setTargetID(target.getID());
eventDispatcher.dispatch(new CharacterTargetSelectedEvent( eventDispatcher.dispatch(new CharacterTargetSelectedEvent(
character, target)); character, target));
conn.write(new SM_TARGET(target, character.getLevel()
- target.getLevel()));
} else { } else {
// this indicates an inconsistency: reset target and throws an // this indicates an inconsistency: reset target and throws an
// exception // exception
@@ -416,8 +312,6 @@ public class CharacterServiceImpl extends AbstractService implements
log.debug("Character {} is trying to attack {}", character, target); 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 // check if this Actor can be attacked
if (target instanceof NPC) { if (target instanceof NPC) {
final NPC npc = (NPC) target; final NPC npc = (NPC) target;
@@ -431,11 +325,11 @@ public class CharacterServiceImpl extends AbstractService implements
// now attack the npc // now attack the npc
log.debug("Sending {} attack request to NPCService", character); log.debug("Sending {} attack request to NPCService", character);
npcService.attack(npc, conn, character); npcService.attack(npc, character);
} else { } else {
// TODO throw an exception throw new ActorIsNotAttackableServiceException();
conn.sendActionFailed();
} }
characterDao.save(character);
} }
@Override @Override
@@ -463,20 +357,17 @@ public class CharacterServiceImpl extends AbstractService implements
log.debug("{} is moving to {}", character, coordinate); 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 // we don't set the character coordinate here, this will be done by
// validation packets, sent by client // validation packets, sent by client
character.setState(ActorState.MOVING); character.setState(ActorState.MOVING);
character.setTargetLocation(coordinate.toPoint()); character.setTargetLocation(coordinate.toPoint());
// for now, let's just write the packet, we don't have much validation // dispatch the start moving event. BroadcastService will catch it and
// to be done yet. With character validation packet, another packet of // notify the client.
// these will be broadcasted. eventDispatcher.dispatch(new CharacterStartMovingEvent(character,
conn.write(new SM_ACTOR_MOVE(character, coordinate)); coordinate.toPoint()));
// we don't dispatch events here, they will be dispatched by characterDao.save(character);
// with the same packet referred up here.
} }
@Override @Override
@@ -519,8 +410,6 @@ public class CharacterServiceImpl extends AbstractService implements
public void walk(L2Character character) public void walk(L2Character character)
throws CharacterAlreadyWalkingServiceException { throws CharacterAlreadyWalkingServiceException {
Preconditions.checkNotNull(character, "character"); Preconditions.checkNotNull(character, "character");
final CharacterID id = character.getID();
final Lineage2Client conn = networkService.discover(id);
// test if character is running // test if character is running
if (character.getMoveType() == CharacterMoveType.WALK) if (character.getMoveType() == CharacterMoveType.WALK)
throw new CharacterAlreadyWalkingServiceException(); throw new CharacterAlreadyWalkingServiceException();
@@ -531,15 +420,12 @@ public class CharacterServiceImpl extends AbstractService implements
character.setMoveType(CharacterMoveType.WALK); character.setMoveType(CharacterMoveType.WALK);
eventDispatcher.dispatch(new CharacterWalkingEvent(character)); eventDispatcher.dispatch(new CharacterWalkingEvent(character));
conn.write(new SM_MOVE_TYPE(character));
} }
@Override @Override
public void run(L2Character character) public void run(L2Character character)
throws CharacterAlreadyRunningServiceException { throws CharacterAlreadyRunningServiceException {
Preconditions.checkNotNull(character, "character"); Preconditions.checkNotNull(character, "character");
final CharacterID id = character.getID();
final Lineage2Client conn = networkService.discover(id);
// test if character is walking // test if character is walking
if (character.getMoveType() == CharacterMoveType.RUN) if (character.getMoveType() == CharacterMoveType.RUN)
throw new CharacterAlreadyRunningServiceException(); throw new CharacterAlreadyRunningServiceException();
@@ -550,6 +436,5 @@ public class CharacterServiceImpl extends AbstractService implements
character.setMoveType(CharacterMoveType.RUN); character.setMoveType(CharacterMoveType.RUN);
eventDispatcher.dispatch(new CharacterRunningEvent(character)); eventDispatcher.dispatch(new CharacterRunningEvent(character));
conn.write(new SM_MOVE_TYPE(character));
} }
} }

View File

@@ -16,7 +16,6 @@
*/ */
package com.l2jserver.service.game.npc; 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.game.net.packet.client.CM_CHAR_ACTION.CharacterAction;
import com.l2jserver.model.template.npc.NPCTemplate; import com.l2jserver.model.template.npc.NPCTemplate;
import com.l2jserver.model.world.Actor; import com.l2jserver.model.world.Actor;
@@ -97,13 +96,11 @@ public interface NPCService extends Service {
* *
* @param npc * @param npc
* the npc * the npc
* @param conn
* the {@link Lineage2Client} object
* @param attacker * @param attacker
* the character * the character
* @throws NotAttackableNPCServiceException * @throws NotAttackableNPCServiceException
* if {@link NPC} is not attackable * if {@link NPC} is not attackable
*/ */
void attack(NPC npc, Lineage2Client conn, L2Character attacker) void attack(NPC npc, L2Character attacker)
throws NotAttackableNPCServiceException; throws NotAttackableNPCServiceException;
} }

View File

@@ -255,10 +255,9 @@ public class NPCServiceImpl extends AbstractService implements NPCService {
} }
@Override @Override
public void attack(NPC npc, Lineage2Client conn, L2Character attacker) public void attack(NPC npc, L2Character attacker)
throws NotAttackableNPCServiceException { throws NotAttackableNPCServiceException {
Preconditions.checkNotNull(npc, "npc"); Preconditions.checkNotNull(npc, "npc");
Preconditions.checkNotNull(conn, "conn");
Preconditions.checkNotNull(attacker, "attacker"); Preconditions.checkNotNull(attacker, "attacker");
log.debug("{} is being attacked by {}", npc, attacker); log.debug("{} is being attacked by {}", npc, attacker);

View File

@@ -19,7 +19,6 @@ package com.l2jserver.service.game.world.event;
import java.util.Map; import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.TimerTask;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; 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() { public void start() {
threadPool = threadService.createThreadPool("event-dispatcher", 1); final int threads = Runtime.getRuntime().availableProcessors();
threadPool.async(0, TimeUnit.MILLISECONDS, 20, new TimerTask() { threadPool = threadService
@Override .createThreadPool("event-dispatcher", threads);
public void run() { for (int i = 0; i < threads; i++) {
EventContainer event; threadPool.async(0, TimeUnit.MILLISECONDS, 10, new Runnable() {
while ((event = events.poll()) != null) { @Override
try { public void run() {
log.debug("Dispatching event {}", event.event); EventContainer event;
while ((event = events.poll()) != null) {
synchronized (event) {
try {
if (event.future.isCancelled())
continue;
// set state log.debug("Dispatching event {}", event.event);
event.future.running = true;
event.future.complete = false;
// dispatch // set state
if (doDispatch(event)) event.future.running = true;
// the set will update state event.future.complete = false;
event.future.set(event.event);
} catch (Throwable t) { // dispatch
event.future.setException(t); doDispatch(event.event);
log.warn("Exception in WorldEventDispatcher thread", t); // 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 @Override
public <E extends WorldEvent> WorldEventFuture<E> dispatch(E event) { public <E extends WorldEvent> WorldEventFuture<E> dispatch(final E event) {
Preconditions.checkNotNull(event, "event"); Preconditions.checkNotNull(event, "event");
log.debug("Queing dispatch for event {}", event); log.debug("Queing dispatch for event {}", event);
final WorldEventFutureImpl<E> future = new WorldEventFutureImpl<E>(); final WorldEventFutureImpl<E> future = new WorldEventFutureImpl<E>();
events.add(new EventContainer(event, future)); events.add(new EventContainer(event, future));
// final WorldEventFutureImpl<E> future = new WorldEventFutureImpl<E>();
// final EventContainer c = new EventContainer(event, future);
// doDispatch(c);
return future; return future;
} }
@@ -129,18 +137,15 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher {
* *
* @param event * @param event
* the event * the event
* @return true if dispatch was not canceled
*/ */
public synchronized boolean doDispatch(EventContainer event) { private synchronized void doDispatch(WorldEvent event) {
final ObjectID<?>[] objects = event.event.getDispatchableObjects(); final ObjectID<?>[] objects = event.getDispatchableObjects();
for (ObjectID<?> obj : objects) { for (ObjectID<?> obj : objects) {
if (obj == null) if (obj == null)
continue; continue;
for (final WorldListener listener : globalListeners) { for (final WorldListener listener : globalListeners) {
if (event.future.isCancelled())
return false;
try { try {
if (!listener.dispatch(event.event)) if (!listener.dispatch(event))
// remove listener if return value is false // remove listener if return value is false
globalListeners.remove(listener); globalListeners.remove(listener);
} catch (Throwable t) { } catch (Throwable t) {
@@ -151,10 +156,8 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher {
} }
final Set<WorldListener> listeners = getListeners(obj); final Set<WorldListener> listeners = getListeners(obj);
for (final WorldListener listener : listeners) { for (final WorldListener listener : listeners) {
if (event.future.isCancelled())
return false;
try { try {
if (!listener.dispatch(event.event)) if (!listener.dispatch(event))
// remove listener if return value is false // remove listener if return value is false
listeners.remove(listener); listeners.remove(listener);
} catch (Throwable t) { } catch (Throwable t) {
@@ -164,7 +167,6 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher {
} }
} }
} }
return true;
} }
@Override @Override
@@ -288,10 +290,10 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher {
} }
@Override @Override
public void await() throws InterruptedException { public void await() throws ExecutionException {
try { try {
super.get(); super.get();
} catch (ExecutionException e) { } catch (InterruptedException e) {
} }
} }

View File

@@ -16,6 +16,7 @@
*/ */
package com.l2jserver.service.game.world.event; package com.l2jserver.service.game.world.event;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@@ -34,10 +35,10 @@ public interface WorldEventFuture<E extends WorldEvent> extends Future<E> {
/** /**
* Waits until the event is dispatched to all listeners * Waits until the event is dispatched to all listeners
* *
* @throws InterruptedException * @throws ExecutionException
* if the thread has been interrupted while waiting * 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 * Waits until the event is dispatched to all listeners

View File

@@ -17,7 +17,7 @@
package com.l2jserver.service.network; package com.l2jserver.service.network;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit;
import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFuture; 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.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.logging.InternalLoggerFactory; import org.jboss.netty.logging.InternalLoggerFactory;
import org.jboss.netty.logging.Slf4JLoggerFactory; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -41,8 +43,12 @@ import com.l2jserver.service.AbstractService;
import com.l2jserver.service.AbstractService.Depends; import com.l2jserver.service.AbstractService.Depends;
import com.l2jserver.service.configuration.ConfigurationService; import com.l2jserver.service.configuration.ConfigurationService;
import com.l2jserver.service.core.LoggingService; 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.game.world.WorldService;
import com.l2jserver.service.network.keygen.BlowfishKeygenService; import com.l2jserver.service.network.keygen.BlowfishKeygenService;
import com.l2jserver.util.ThreadPoolUtils;
import com.l2jserver.util.factory.CollectionFactory; import com.l2jserver.util.factory.CollectionFactory;
/** /**
@@ -50,8 +56,8 @@ import com.l2jserver.util.factory.CollectionFactory;
* *
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
*/ */
@Depends({ LoggingService.class, BlowfishKeygenService.class, @Depends({ LoggingService.class, ThreadService.class,
WorldService.class }) BlowfishKeygenService.class, WorldService.class })
public class NettyNetworkService extends AbstractService implements public class NettyNetworkService extends AbstractService implements
NetworkService { NetworkService {
/** /**
@@ -59,6 +65,11 @@ public class NettyNetworkService extends AbstractService implements
*/ */
private final Logger log = LoggerFactory.getLogger(this.getClass()); private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* The {@link ThreadService}
*/
private final ThreadService threadService;
/** /**
* The network configuration object * The network configuration object
*/ */
@@ -68,6 +79,14 @@ public class NettyNetworkService extends AbstractService implements
*/ */
private final Injector injector; private final Injector injector;
/**
* Netty Boss {@link ThreadPool}
*/
private ThreadPool bossPool;
/**
* Netty Worker {@link ThreadPool}
*/
private ThreadPool workerPool;
/** /**
* The server bootstrap * The server bootstrap
*/ */
@@ -86,10 +105,13 @@ public class NettyNetworkService extends AbstractService implements
* the configuration service * the configuration service
* @param injector * @param injector
* the {@link Guice} {@link Injector} * the {@link Guice} {@link Injector}
* @param threadService
* the {@link ThreadService}
*/ */
@Inject @Inject
public NettyNetworkService(ConfigurationService configService, public NettyNetworkService(ConfigurationService configService,
Injector injector) { Injector injector, ThreadService threadService) {
this.threadService = threadService;
this.config = configService.get(NetworkConfiguration.class); this.config = configService.get(NetworkConfiguration.class);
this.injector = injector; this.injector = injector;
InternalLoggerFactory.setDefaultFactory(new Slf4JLoggerFactory()); InternalLoggerFactory.setDefaultFactory(new Slf4JLoggerFactory());
@@ -97,9 +119,24 @@ public class NettyNetworkService extends AbstractService implements
@Override @Override
protected void doStart() { 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( server = new ServerBootstrap(new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(), ThreadPoolUtils.wrap(bossPool),
Executors.newCachedThreadPool())); ThreadPoolUtils.wrap(workerPool), 50));
server.setPipelineFactory(new Lineage2PipelineFactory(injector, this)); server.setPipelineFactory(new Lineage2PipelineFactory(injector, this));
channel = (ServerChannel) server.bind(config.getListenAddress()); channel = (ServerChannel) server.bind(config.getListenAddress());
} }
@@ -160,9 +197,15 @@ public class NettyNetworkService extends AbstractService implements
protected void doStop() { protected void doStop() {
try { try {
channel.close().awaitUninterruptibly(); channel.close().awaitUninterruptibly();
server.releaseExternalResources();
bossPool.dispose();
workerPool.dispose();
} finally { } finally {
server = null; server = null;
channel = null; channel = null;
bossPool = null;
workerPool = null;
} }
clients.clear();
} }
} }

View File

@@ -16,7 +16,7 @@
*/ */
package com.l2jserver.service.network.broadcast; 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.model.world.WorldObject;
import com.l2jserver.service.Service; import com.l2jserver.service.Service;
@@ -31,7 +31,7 @@ public interface BroadcastService extends Service {
* Broadcast all nearby objects to the given <tt>client</tt> * Broadcast all nearby objects to the given <tt>client</tt>
* *
* @param conn * @param conn
* the Lineage 2 client * the character
*/ */
void broadcast(Lineage2Client conn); void broadcast(L2Character conn);
} }

View File

@@ -23,16 +23,22 @@ import com.google.common.base.Preconditions;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.l2jserver.game.net.Lineage2Client; import com.l2jserver.game.net.Lineage2Client;
import com.l2jserver.game.net.SystemMessage; import com.l2jserver.game.net.SystemMessage;
import com.l2jserver.game.net.packet.server.SM_ATTACK; import com.l2jserver.game.net.packet.server.SM_ACTOR_CHAT;
import com.l2jserver.game.net.packet.server.SM_CHAR_INFO_BROADCAST;
import com.l2jserver.game.net.packet.server.SM_ACTOR_DIE; 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_GROUND;
import com.l2jserver.game.net.packet.server.SM_ITEM_PICK; 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_MOVE_TYPE;
import com.l2jserver.game.net.packet.server.SM_NPC_INFO; 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_OBJECT_REMOVE;
import com.l2jserver.game.net.packet.server.SM_TARGET;
import com.l2jserver.model.id.object.CharacterID; import com.l2jserver.model.id.object.CharacterID;
import com.l2jserver.model.server.ChatMessage;
import com.l2jserver.model.world.Item; import com.l2jserver.model.world.Item;
import com.l2jserver.model.world.L2Character; import com.l2jserver.model.world.L2Character;
import com.l2jserver.model.world.NPC; 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.ActorTeleportingEvent;
import com.l2jserver.model.world.actor.event.ActorUnspawnEvent; import com.l2jserver.model.world.actor.event.ActorUnspawnEvent;
import com.l2jserver.model.world.character.event.CharacterEnterWorldEvent; 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.CharacterLeaveWorldEvent;
import com.l2jserver.model.world.character.event.CharacterListener;
import com.l2jserver.model.world.character.event.CharacterMoveEvent; import com.l2jserver.model.world.character.event.CharacterMoveEvent;
import com.l2jserver.model.world.character.event.CharacterRunningEvent; 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.character.event.CharacterWalkingEvent;
import com.l2jserver.model.world.item.ItemDropEvent; import com.l2jserver.model.world.item.ItemDropEvent;
import com.l2jserver.model.world.item.ItemPickEvent; 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.model.world.player.event.PlayerTeleportedEvent;
import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService;
import com.l2jserver.service.AbstractService.Depends; 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.WorldService;
import com.l2jserver.service.game.world.event.FilteredWorldListener; import com.l2jserver.service.game.world.event.FilteredWorldListener;
import com.l2jserver.service.game.world.event.WorldEvent; import com.l2jserver.service.game.world.event.WorldEvent;
@@ -78,6 +92,15 @@ public class BroadcastServiceImpl extends AbstractService implements
* The world service * The world service
*/ */
private final WorldService worldService; private final WorldService worldService;
/**
* The {@link ChatService}
*/
private final ChatService chatService;
/**
* The {@link ChatService}
*/
private final NetworkService networkService;
/** /**
* The world service event dispatcher * The world service event dispatcher
*/ */
@@ -86,21 +109,28 @@ public class BroadcastServiceImpl extends AbstractService implements
/** /**
* @param worldService * @param worldService
* the world service * the world service
* @param chatService
* the chat service
* @param networkService
* the network service
* @param eventDispatcher * @param eventDispatcher
* the world service event disptacher * the world service event disptacher
*/ */
@Inject @Inject
public BroadcastServiceImpl(WorldService worldService, public BroadcastServiceImpl(WorldService worldService,
ChatService chatService, NetworkService networkService,
WorldEventDispatcher eventDispatcher) { WorldEventDispatcher eventDispatcher) {
this.worldService = worldService; this.worldService = worldService;
this.chatService = chatService;
this.networkService = networkService;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
} }
@Override @Override
public void broadcast(final Lineage2Client conn) { public void broadcast(final L2Character character) {
Preconditions.checkNotNull(conn, "conn");
final L2Character character = conn.getCharacter();
Preconditions.checkNotNull(character, "character"); Preconditions.checkNotNull(character, "character");
final Lineage2Client conn = networkService.discover(character.getID());
Preconditions.checkNotNull(conn, "conn");
final CharacterID id = character.getID(); final CharacterID id = character.getID();
log.debug("Starting character broadcast"); log.debug("Starting character broadcast");
@@ -121,8 +151,8 @@ public class BroadcastServiceImpl extends AbstractService implements
broadcast(conn, e.getObject()); broadcast(conn, e.getObject());
} else if (e instanceof CharacterMoveEvent) { } else if (e instanceof CharacterMoveEvent) {
final CharacterMoveEvent evt = (CharacterMoveEvent) e; final CharacterMoveEvent evt = (CharacterMoveEvent) e;
conn.write(new SM_ACTOR_MOVE((L2Character) object, evt.getPoint() conn.write(new SM_ACTOR_MOVE((L2Character) object, evt
.getCoordinate())); .getPoint().getCoordinate()));
} else if (e instanceof PlayerTeleportedEvent } else if (e instanceof PlayerTeleportedEvent
|| e instanceof CharacterEnterWorldEvent) { || e instanceof CharacterEnterWorldEvent) {
broadcast(conn, e.getObject()); broadcast(conn, e.getObject());
@@ -134,7 +164,6 @@ public class BroadcastServiceImpl extends AbstractService implements
|| e instanceof CharacterLeaveWorldEvent || e instanceof CharacterLeaveWorldEvent
|| e instanceof ActorUnspawnEvent) { || e instanceof ActorUnspawnEvent) {
// object is now out of sight // object is now out of sight
// FIXME pick up animation is not happening
conn.write(new SM_OBJECT_REMOVE(object)); conn.write(new SM_OBJECT_REMOVE(object));
} else if (e instanceof CharacterWalkingEvent) { } else if (e instanceof CharacterWalkingEvent) {
conn.write(new SM_MOVE_TYPE(((CharacterWalkingEvent) e) conn.write(new SM_MOVE_TYPE(((CharacterWalkingEvent) e)
@@ -160,15 +189,93 @@ public class BroadcastServiceImpl extends AbstractService implements
if (e instanceof CharacterMoveEvent) { if (e instanceof CharacterMoveEvent) {
final CharacterMoveEvent evt = (CharacterMoveEvent) e; final CharacterMoveEvent evt = (CharacterMoveEvent) e;
// process update known list // process update known list
broadcastUpdate(conn, character, evt.getPoint()); broadcastUpdate(conn, evt.getCharacter(), evt.getPoint());
} else if (e instanceof PlayerTeleportedEvent } else if (e instanceof CharacterEnterWorldEvent) {
|| 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); broadcastAll(conn, character);
} else if (e instanceof ActorAttackHitEvent) { } else if (e instanceof ActorAttackHitEvent) {
conn.write(new SM_ATTACK(((ActorAttackHitEvent) e).getHit())); conn.write(new SM_ATTACK(((ActorAttackHitEvent) e).getHit()));
conn.sendSystemMessage(SystemMessage.YOU_DID_S1_DMG, conn.sendSystemMessage(SystemMessage.YOU_DID_S1_DMG,
(int) ((ActorAttackHitEvent) e).getHit() (int) ((ActorAttackHitEvent) e).getHit()
.getDamage()); .getDamage());
} else if (e instanceof CharacterWalkingEvent
|| e instanceof CharacterRunningEvent) {
conn.write(new SM_MOVE_TYPE((L2Character) e.getObject()));
} }
// keep listener alive // keep listener alive
return true; return true;

View File

@@ -19,6 +19,7 @@ package com.l2jserver.service.network.gameguard;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import com.l2jserver.game.net.Lineage2Client; import com.l2jserver.game.net.Lineage2Client;
import com.l2jserver.model.world.L2Character;
import com.l2jserver.service.Service; import com.l2jserver.service.Service;
/** /**
@@ -30,11 +31,11 @@ public interface GameGuardService extends Service {
/** /**
* Queries the client GameGuard for an response * Queries the client GameGuard for an response
* *
* @param conn * @param character
* the lineage 2 connection * the lineage 2 character
* @return an future that will be used to obtain validation status * @return an future that will be used to obtain validation status
*/ */
Future<GameGuardResponse> query(Lineage2Client conn); Future<GameGuardResponse> query(L2Character character);
/** /**
* The Game guard key state * The Game guard key state

View File

@@ -27,12 +27,15 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.AbstractFuture;
import com.google.inject.Inject;
import com.l2jserver.game.net.Lineage2Client; import com.l2jserver.game.net.Lineage2Client;
import com.l2jserver.game.net.packet.server.SM_GG_QUERY; 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;
import com.l2jserver.service.AbstractService.Depends; import com.l2jserver.service.AbstractService.Depends;
import com.l2jserver.service.ServiceStartException; import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException; import com.l2jserver.service.ServiceStopException;
import com.l2jserver.service.game.chat.ChatService;
import com.l2jserver.service.network.NetworkService; import com.l2jserver.service.network.NetworkService;
import com.l2jserver.util.factory.CollectionFactory; 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) 0xde, (byte) 0xc3, 0x68, (byte) 0xf6, 0x2d, 0x23,
(byte) 0xf1, 0x3f, (byte) 0xee, 0x68, 0x5b, (byte) 0xc5 }; (byte) 0xf1, 0x3f, (byte) 0xee, 0x68, 0x5b, (byte) 0xc5 };
/**
* The {@link ChatService}
*/
private final NetworkService networkService;
/** /**
* The map containing all pending futures * The map containing all pending futures
*/ */
@@ -76,6 +84,15 @@ public class GameGuardServiceImpl extends AbstractService implements
@SuppressWarnings("unused") @SuppressWarnings("unused")
private MessageDigest digester; private MessageDigest digester;
/**
* @param networkService
* the network service
*/
@Inject
private GameGuardServiceImpl(NetworkService networkService) {
this.networkService = networkService;
}
@Override @Override
protected void doStart() throws ServiceStartException { protected void doStart() throws ServiceStartException {
futures = CollectionFactory.newMap(); futures = CollectionFactory.newMap();
@@ -87,8 +104,9 @@ public class GameGuardServiceImpl extends AbstractService implements
} }
@Override @Override
public Future<GameGuardResponse> query(final Lineage2Client conn) { public Future<GameGuardResponse> query(final L2Character character) {
log.debug("Quering client for GameGuard authentication key"); log.debug("Quering client for GameGuard authentication key");
final Lineage2Client conn = networkService.discover(character.getID());
conn.write(new SM_GG_QUERY(STATIC_KEY)).addListener( conn.write(new SM_GG_QUERY(STATIC_KEY)).addListener(
new ChannelFutureListener() { new ChannelFutureListener() {
@Override @Override