Added initial version
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package ms.net;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Used for producing I/O events.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public interface EventProducer {
|
||||
|
||||
/**
|
||||
* Produces a new read event.
|
||||
* @param session The session.
|
||||
* @param buffer The buffer to read.
|
||||
* @return The read event handler.
|
||||
*/
|
||||
IoReadEvent produceReader(IoSession session, ByteBuffer buffer);
|
||||
|
||||
/**
|
||||
* Produces a new writing event.
|
||||
* @param session The session.
|
||||
* @param context The context.
|
||||
* @return The write event handler.
|
||||
*/
|
||||
IoWriteEvent produceWriter(IoSession session, Object context);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package ms.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
/**
|
||||
* I/O event handling.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public final class IoEventHandler {
|
||||
|
||||
/**
|
||||
* The executor service.
|
||||
*/
|
||||
private final ExecutorService service;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code IoEventHandler}.
|
||||
* @param service The executor service used for handling events.
|
||||
*/
|
||||
public IoEventHandler(ExecutorService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when making a new connection.
|
||||
* @param key The selection key.
|
||||
*/
|
||||
public void connect(SelectionKey key) {
|
||||
/*
|
||||
* empty.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for accepting a new connection.
|
||||
* @param key The selection key.
|
||||
* @param selector The selector.
|
||||
* @throws IOException When an I/O exception occurs.
|
||||
*/
|
||||
public void accept(SelectionKey key, Selector selector) throws IOException {
|
||||
SocketChannel sc = ((ServerSocketChannel) key.channel()).accept();
|
||||
sc.configureBlocking(false);
|
||||
sc.socket().setTcpNoDelay(true);
|
||||
sc.register(selector, SelectionKey.OP_READ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the incoming packet data.
|
||||
* @param key The selection key.
|
||||
* @throws IOException When an I/O exception occurs.
|
||||
*/
|
||||
public void read(SelectionKey key) throws IOException {
|
||||
ReadableByteChannel channel = (ReadableByteChannel) key.channel();
|
||||
ByteBuffer buffer = ByteBuffer.allocate(100_000);
|
||||
IoSession session = (IoSession) key.attachment();
|
||||
if (channel.read(buffer) == -1) {
|
||||
System.out.println("Existing session disconnected - likely portscanner or server status checker.");
|
||||
key.cancel();
|
||||
return;
|
||||
}
|
||||
buffer.flip();
|
||||
if (session == null) {
|
||||
key.attach(session = new IoSession(key, service));
|
||||
}
|
||||
service.execute(session.getProducer().produceReader(session, buffer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the outgoing packet data.
|
||||
* @param key The selection key.
|
||||
*/
|
||||
public void write(SelectionKey key) {
|
||||
IoSession session = (IoSession) key.attachment();
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
session.write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects a connection.
|
||||
* @param key The selection key.
|
||||
* @param t The occurred exception (if any).
|
||||
*/
|
||||
public void disconnect(SelectionKey key, Throwable t) {
|
||||
try {
|
||||
IoSession session = (IoSession) key.attachment();
|
||||
String cause = "" + t;
|
||||
if (t != null && !(t instanceof ClosedChannelException || cause.contains("De externe host") || cause.contains("De software op uw") || cause.contains("An established connection was aborted") || cause.contains("An existing connection") || cause.contains("AsynchronousClose"))) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
if (session != null) {
|
||||
session.disconnect();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package ms.net;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Handles a reading event.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public abstract class IoReadEvent implements Runnable {
|
||||
|
||||
/**
|
||||
* The I/O session.
|
||||
*/
|
||||
private final IoSession session;
|
||||
|
||||
/**
|
||||
* The buffer.
|
||||
*/
|
||||
private ByteBuffer buffer;
|
||||
|
||||
/**
|
||||
* If the queued reading buffer was used (debugging purposes).
|
||||
*/
|
||||
protected boolean usedQueuedBuffer;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code IoReadEvent}.
|
||||
* @param session The session.
|
||||
* @param buffer The buffer to read from.
|
||||
*/
|
||||
public IoReadEvent(IoSession session, ByteBuffer buffer) {
|
||||
this.session = session;
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (session.getReadingQueue() != null) {
|
||||
buffer = session.getReadingQueue().put(buffer);
|
||||
buffer.flip();
|
||||
session.setReadingQueue(null);
|
||||
usedQueuedBuffer = true;
|
||||
}
|
||||
read(session, buffer);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues the buffer until more data has been received.
|
||||
* @param data The data that has been read already.
|
||||
*/
|
||||
public void queueBuffer(int...data) {
|
||||
ByteBuffer queue = ByteBuffer.allocate(data.length + buffer.remaining() + 100_000);
|
||||
for (int value : data) {
|
||||
queue.put((byte) value);
|
||||
}
|
||||
queue.put(buffer);
|
||||
session.setReadingQueue(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the data from the buffer.
|
||||
* @param session The session.
|
||||
* @param buffer The buffer to read from.
|
||||
*/
|
||||
public abstract void read(IoSession session, ByteBuffer buffer);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
package ms.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import ms.ServerConstants;
|
||||
import ms.net.producer.HSEventProducer;
|
||||
import ms.world.GameServer;
|
||||
|
||||
/**
|
||||
* Represents a connected I/O session.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public class IoSession {
|
||||
|
||||
/**
|
||||
* The handshake event producer.
|
||||
*/
|
||||
private static final EventProducer HANDSHAKE_PRODUCER = new HSEventProducer();
|
||||
|
||||
/**
|
||||
* The selection key.
|
||||
*/
|
||||
private final SelectionKey key;
|
||||
|
||||
/**
|
||||
* The executor service.
|
||||
*/
|
||||
private final ExecutorService service;
|
||||
|
||||
/**
|
||||
* The event producer.
|
||||
*/
|
||||
private EventProducer producer = HANDSHAKE_PRODUCER;
|
||||
|
||||
/**
|
||||
* The currently queued writing data.
|
||||
*/
|
||||
private List<ByteBuffer> writingQueue = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The currently queued reading data.
|
||||
*/
|
||||
private ByteBuffer readingQueue;
|
||||
|
||||
/**
|
||||
* The writing lock.
|
||||
*/
|
||||
private Lock writingLock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* The name hash.
|
||||
*/
|
||||
private int nameHash;
|
||||
|
||||
/**
|
||||
* The server key.
|
||||
*/
|
||||
private long serverKey;
|
||||
|
||||
/**
|
||||
* The JS-5 encryption value.
|
||||
*/
|
||||
private int js5Encryption;
|
||||
|
||||
/**
|
||||
* If the session is active.
|
||||
*/
|
||||
private boolean active = true;
|
||||
|
||||
/**
|
||||
* The last ping time stamp.
|
||||
*/
|
||||
private long lastPing;
|
||||
|
||||
/**
|
||||
* The address.
|
||||
*/
|
||||
private final String address;
|
||||
|
||||
/**
|
||||
* The game server object for this session.
|
||||
*/
|
||||
private GameServer gameServer;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code IoSession}.
|
||||
* @param key The selection key.
|
||||
* @param service The executor service.
|
||||
*/
|
||||
public IoSession(SelectionKey key, ExecutorService service) {
|
||||
this.key = key;
|
||||
this.service = service;
|
||||
String address = getRemoteAddress().replaceAll("/", "").split(":")[0];
|
||||
if (address.equals("127.0.0.1")) {
|
||||
address = ServerConstants.HOST_ADDRESS;
|
||||
}
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires a write event created using the current event producer.
|
||||
* @param context The event context.
|
||||
*/
|
||||
public void write(Object context) {
|
||||
write(context, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires a write event created using the current event producer.
|
||||
* @param context The event context.
|
||||
* @param instant If the event should be instantly executed on this thread.
|
||||
*/
|
||||
public void write(Object context, boolean instant) {
|
||||
if (context == null) {
|
||||
throw new IllegalStateException("Invalid writing context!");
|
||||
}
|
||||
if (instant) {
|
||||
producer.produceWriter(this, context).run();
|
||||
return;
|
||||
}
|
||||
service.execute(producer.produceWriter(this, context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the packet data (without write event encoding).
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
public void queue(ByteBuffer buffer) {
|
||||
try {
|
||||
writingLock.tryLock(1000L, TimeUnit.MILLISECONDS);
|
||||
} catch (Exception e){
|
||||
System.out.println(e);
|
||||
writingLock.unlock();
|
||||
return;
|
||||
}
|
||||
writingQueue.add(buffer);
|
||||
writingLock.unlock();
|
||||
write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the writing of all buffers in the queue.
|
||||
*/
|
||||
public void write() {
|
||||
if (!key.isValid()) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
writingLock.lock();
|
||||
SocketChannel channel = (SocketChannel) key.channel();
|
||||
try {
|
||||
while (!writingQueue.isEmpty()) {
|
||||
ByteBuffer buffer = writingQueue.get(0);
|
||||
channel.write(buffer);
|
||||
if (buffer.hasRemaining()) {
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||
break;
|
||||
}
|
||||
writingQueue.remove(0);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
disconnect();
|
||||
}
|
||||
writingLock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects the session.
|
||||
*/
|
||||
public void disconnect() {
|
||||
try {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
active = false;
|
||||
key.cancel();
|
||||
SocketChannel channel = (SocketChannel) key.channel();
|
||||
channel.socket().close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the IP-address (without the port).
|
||||
* @return The address.
|
||||
*/
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remote address of this session.
|
||||
* @return The remote address, as a String.
|
||||
*/
|
||||
public String getRemoteAddress() {
|
||||
try {
|
||||
return ((SocketChannel) key.channel()).getRemoteAddress().toString();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current event producer.
|
||||
* @return The producer.
|
||||
*/
|
||||
public EventProducer getProducer() {
|
||||
return producer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event producer.
|
||||
* @param producer The producer to set.
|
||||
*/
|
||||
public void setProducer(EventProducer producer) {
|
||||
this.producer = producer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the queued reading data.
|
||||
* @return The readingQueue.
|
||||
*/
|
||||
public ByteBuffer getReadingQueue() {
|
||||
synchronized (this) {
|
||||
return readingQueue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues reading data.
|
||||
* @param readingQueue The readingQueue to set.
|
||||
*/
|
||||
public void setReadingQueue(ByteBuffer readingQueue) {
|
||||
synchronized (this) {
|
||||
this.readingQueue = readingQueue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the writing lock.
|
||||
* @return The writing lock.
|
||||
*/
|
||||
public Lock getWritingLock() {
|
||||
return writingLock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the selection key.
|
||||
* @return The selection key.
|
||||
*/
|
||||
public SelectionKey getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The active.
|
||||
*/
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The js5Encryption.
|
||||
*/
|
||||
public int getJs5Encryption() {
|
||||
return js5Encryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param js5Encryption The js5Encryption to set.
|
||||
*/
|
||||
public void setJs5Encryption(int js5Encryption) {
|
||||
this.js5Encryption = js5Encryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the lastPing.
|
||||
* @return The lastPing.
|
||||
*/
|
||||
public long getLastPing() {
|
||||
return lastPing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the lastPing.
|
||||
* @param lastPing The lastPing to set.
|
||||
*/
|
||||
public void setLastPing(long lastPing) {
|
||||
this.lastPing = lastPing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nameHash.
|
||||
* @return The nameHash.
|
||||
*/
|
||||
public int getNameHash() {
|
||||
return nameHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the nameHash.
|
||||
* @param nameHash The nameHash to set.
|
||||
*/
|
||||
public void setNameHash(int nameHash) {
|
||||
this.nameHash = nameHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the serverKey.
|
||||
* @return The serverKey.
|
||||
*/
|
||||
public long getServerKey() {
|
||||
return serverKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the serverKey.
|
||||
* @param serverKey The serverKey to set.
|
||||
*/
|
||||
public void setServerKey(long serverKey) {
|
||||
this.serverKey = serverKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the gameServer value.
|
||||
* @return The gameServer.
|
||||
*/
|
||||
public GameServer getGameServer() {
|
||||
return gameServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the gameServer value.
|
||||
* @param gameServer The gameServer to set.
|
||||
*/
|
||||
public void setGameServer(GameServer gameServer) {
|
||||
this.gameServer = gameServer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package ms.net;
|
||||
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
|
||||
/**
|
||||
* Handles a writing event.
|
||||
* @author Emperor
|
||||
*/
|
||||
public abstract class IoWriteEvent implements Runnable {
|
||||
|
||||
/**
|
||||
* The I/O session.
|
||||
*/
|
||||
private final IoSession session;
|
||||
|
||||
/**
|
||||
* The buffer.
|
||||
*/
|
||||
private final Object context;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code IoWriteEvent}.
|
||||
* @param session The session.
|
||||
* @param context The write event context.
|
||||
*/
|
||||
public IoWriteEvent(IoSession session, Object context) {
|
||||
this.session = session;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
write(session, context);
|
||||
} catch (Throwable t) {
|
||||
if (!(t instanceof CancelledKeyException)) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the data.
|
||||
* @param session The session.
|
||||
* @param context The write event context.
|
||||
*/
|
||||
public abstract void write(IoSession session, Object context);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package ms.net;
|
||||
|
||||
import ms.Management;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Handles (NIO-based) networking events using the reactor pattern.
|
||||
* @author Emperor
|
||||
*/
|
||||
public final class NioReactor implements Runnable {
|
||||
|
||||
/**
|
||||
* The executor service.
|
||||
*/
|
||||
private final ExecutorService service;
|
||||
|
||||
/**
|
||||
* The selector
|
||||
*/
|
||||
private Selector selector;
|
||||
|
||||
/**
|
||||
* The socket channel.
|
||||
*/
|
||||
private ServerSocketChannel channel;
|
||||
|
||||
/**
|
||||
* The I/O event handling instance.
|
||||
*/
|
||||
private final IoEventHandler eventHandler;
|
||||
|
||||
/**
|
||||
* If the reactor is running.
|
||||
*/
|
||||
private boolean running;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code NioReactor}.
|
||||
* @param poolSize The pool size.
|
||||
*/
|
||||
private NioReactor(int poolSize) {
|
||||
this.service = Executors.newSingleThreadScheduledExecutor();
|
||||
this.eventHandler = new IoEventHandler(Executors.newFixedThreadPool(poolSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and configures a new {@code NioReactor} with a pool size of 1.
|
||||
* @param port The port.
|
||||
* @return The {@code NioReactor} {@code Object}.
|
||||
* @throws IOException When an I/O exception occurs.
|
||||
*/
|
||||
public static NioReactor configure(int port) throws IOException {
|
||||
return configure(port, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and configures a new {@code NioReactor}.
|
||||
* @param port The port.
|
||||
* @param poolSize The amount of threads in the thread pool.
|
||||
* @return The {@code NioReactor} {@code Object}.
|
||||
* @throws IOException When an I/O exception occurs.
|
||||
*/
|
||||
public static NioReactor configure(int port, int poolSize) throws IOException {
|
||||
NioReactor reactor = new NioReactor(poolSize);
|
||||
reactor.channel = ServerSocketChannel.open();
|
||||
reactor.selector = Selector.open();
|
||||
reactor.channel.bind(new InetSocketAddress(port));
|
||||
reactor.channel.configureBlocking(false);
|
||||
reactor.channel.register(reactor.selector, SelectionKey.OP_ACCEPT);
|
||||
return reactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the reactor.
|
||||
*/
|
||||
public void start() {
|
||||
running = true;
|
||||
service.execute(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (running && Management.active) {
|
||||
try {
|
||||
if(selector.select() > 0){
|
||||
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
|
||||
while (keys.hasNext()) {
|
||||
SelectionKey key = keys.next();
|
||||
keys.remove();
|
||||
try {
|
||||
if (!key.isValid() || !key.channel().isOpen()) {
|
||||
key.cancel();
|
||||
continue;
|
||||
}
|
||||
if (key.isAcceptable()) {
|
||||
eventHandler.accept(key, selector);
|
||||
}
|
||||
if (key.isReadable()) {
|
||||
eventHandler.read(key);
|
||||
}
|
||||
else if (key.isWritable()) {
|
||||
eventHandler.write(key);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
eventHandler.disconnect(key, t);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
System.out.println("SLEEPING");
|
||||
Thread.sleep(200);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the reactor (once it's done processing current I/O events).
|
||||
*/
|
||||
public void terminate() {
|
||||
running = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package ms.net.event;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import ms.net.IoReadEvent;
|
||||
import ms.net.IoSession;
|
||||
import ms.world.WorldDatabase;
|
||||
import ms.system.util.ByteBufferUtils;
|
||||
|
||||
/**
|
||||
* Handles handshake read events.
|
||||
* @author Emperor
|
||||
*/
|
||||
public final class HSReadEvent extends IoReadEvent {
|
||||
|
||||
/**
|
||||
* The password used to verify
|
||||
*/
|
||||
private static final String PASSWORD = "0x14ari0SSbh98989910";
|
||||
|
||||
/**
|
||||
* Constructs a new {@code HSReadEvent}.
|
||||
* @param session The session.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
public HSReadEvent(IoSession session, ByteBuffer buffer) {
|
||||
super(session, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(IoSession session, ByteBuffer buffer) {
|
||||
int opcode = buffer.get() & 0xFF;
|
||||
switch (opcode) {
|
||||
case 88:
|
||||
String password = ByteBufferUtils.getString(buffer);
|
||||
if (!password.equals(PASSWORD)) {
|
||||
System.out.println("Password mismatch (attempt=" + password + ")!");
|
||||
session.disconnect();
|
||||
break;
|
||||
}
|
||||
session.write(opcode);
|
||||
break;
|
||||
case 255: // World list
|
||||
int updateStamp = buffer.getInt();
|
||||
WorldDatabase.sendUpdate(session, updateStamp);
|
||||
break;
|
||||
default:
|
||||
System.err.println("Unhandled handshake opcode: " + opcode + ".");
|
||||
session.disconnect();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package ms.net.event;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import ms.net.IoSession;
|
||||
import ms.net.IoWriteEvent;
|
||||
import ms.net.producer.RegistryEventProducer;
|
||||
|
||||
/**
|
||||
* Handles Handshake write events.
|
||||
* @author Emperor
|
||||
*/
|
||||
public final class HSWriteEvent extends IoWriteEvent {
|
||||
|
||||
/**
|
||||
* The login event producer.
|
||||
*/
|
||||
private static final RegistryEventProducer REGISTRY_PRODUCER = new RegistryEventProducer();
|
||||
|
||||
/**
|
||||
* Constructs a new {@code HSWriteEvent} {@code Object}.
|
||||
* @param session The session.
|
||||
* @param context The context.
|
||||
*/
|
||||
public HSWriteEvent(IoSession session, Object context) {
|
||||
super(session, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(IoSession session, Object context) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(9);
|
||||
buffer.put((byte) 14);
|
||||
session.setProducer( REGISTRY_PRODUCER);
|
||||
buffer.flip();
|
||||
session.queue(buffer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package ms.net.event;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import ms.net.IoReadEvent;
|
||||
import ms.net.IoSession;
|
||||
import ms.net.packet.WorldPacketRepository;
|
||||
|
||||
/**
|
||||
* Handles world packet reading events.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public final class PacketReadEvent extends IoReadEvent {
|
||||
|
||||
/**
|
||||
* The packet sizes.
|
||||
*/
|
||||
private static final int[] PACKET_SIZE = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a new {@code PacketReadEvent} {@code Object}.
|
||||
* @param session The I/O session.
|
||||
* @param buffer The buffer to read from.
|
||||
*/
|
||||
public PacketReadEvent(IoSession session, ByteBuffer buffer) {
|
||||
super(session, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(IoSession session, ByteBuffer buffer) {
|
||||
int last = -1;
|
||||
while (buffer.hasRemaining()) {
|
||||
int opcode = buffer.get() & 0xFF;
|
||||
if (opcode >= PACKET_SIZE.length) {
|
||||
break;
|
||||
}
|
||||
int header = PACKET_SIZE[opcode];
|
||||
int size = header;
|
||||
if (header < 0) {
|
||||
size = getPacketSize(buffer, opcode, header, last);
|
||||
}
|
||||
if (size == -1) {
|
||||
break;
|
||||
}
|
||||
if (buffer.remaining() < size) {
|
||||
switch (header) {
|
||||
case -2:
|
||||
queueBuffer(opcode, size >> 8, size);
|
||||
break;
|
||||
case -1:
|
||||
queueBuffer(opcode, size);
|
||||
break;
|
||||
default:
|
||||
queueBuffer(opcode);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
byte[] data = new byte[size];
|
||||
buffer.get(data);
|
||||
last = opcode;
|
||||
try {
|
||||
WorldPacketRepository.handleIncoming(session, opcode, ByteBuffer.wrap(data));
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the packet size for the given opcode.
|
||||
* @param buffer The buffer.
|
||||
* @param opcode The opcode.
|
||||
* @param header The packet header.
|
||||
* @param last The last opcode.
|
||||
* @return The packet size.
|
||||
*/
|
||||
private int getPacketSize(ByteBuffer buffer, int opcode, int header, int last) {
|
||||
if (header == -1) {
|
||||
if (buffer.remaining() < 1) {
|
||||
queueBuffer(opcode);
|
||||
return -1;
|
||||
}
|
||||
return buffer.get() & 0xFF;
|
||||
}
|
||||
if (header == -2) {
|
||||
if (buffer.remaining() < 2) {
|
||||
queueBuffer(opcode);
|
||||
return -1;
|
||||
}
|
||||
return buffer.getShort() & 0xFFFF;
|
||||
}
|
||||
System.err.println("Invalid packet [opcode=" + opcode + ", last=" + last + ", queued=" + usedQueuedBuffer + "]!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package ms.net.event;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import ms.net.IoSession;
|
||||
import ms.net.IoWriteEvent;
|
||||
import ms.net.packet.IoBuffer;
|
||||
|
||||
/**
|
||||
* Handles world packet writing events.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public final class PacketWriteEvent extends IoWriteEvent {
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new {@code PacketWriteEvent} {@code Object}.
|
||||
* @param session The I/O session.
|
||||
* @param context The packet context.
|
||||
*/
|
||||
public PacketWriteEvent(IoSession session, Object context) {
|
||||
super(session, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(IoSession session, Object context) {
|
||||
IoBuffer b = (IoBuffer) context;
|
||||
int size = b.toByteBuffer().position();
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1 + size + b.getHeader().ordinal());
|
||||
buffer.put((byte) b.opcode());
|
||||
switch (b.getHeader()) {
|
||||
case NORMAL:
|
||||
break;
|
||||
case BYTE:
|
||||
buffer.put((byte) size);
|
||||
break;
|
||||
case SHORT:
|
||||
buffer.putShort((short) size);
|
||||
break;
|
||||
}
|
||||
buffer.put((ByteBuffer) b.toByteBuffer().flip());
|
||||
session.queue((ByteBuffer) buffer.flip());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package ms.net.event;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import ms.ServerConstants;
|
||||
import ms.net.IoReadEvent;
|
||||
import ms.net.IoSession;
|
||||
import ms.world.WorldDatabase;
|
||||
import ms.system.util.ByteBufferUtils;
|
||||
import ms.world.info.CountryFlag;
|
||||
import ms.world.info.WorldInfo;
|
||||
|
||||
/**
|
||||
* Handles world registry read events.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public final class RegistryReadEvent extends IoReadEvent {
|
||||
|
||||
/**
|
||||
* The string check.
|
||||
*/
|
||||
private static final String CHECK = "12x4578f5g45hrdjiofed59898";
|
||||
//kratos = 666x14x88x28shhhwpwwb&h
|
||||
//12x4578f5g45hrdjiofed59898
|
||||
|
||||
/**
|
||||
* Constructs a new {@code RegistryReadEvent} {@code Object}.
|
||||
* @param session The session.
|
||||
* @param buffer The buffer to read.
|
||||
*/
|
||||
public RegistryReadEvent(IoSession session, ByteBuffer buffer) {
|
||||
super(session, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(IoSession session, ByteBuffer buffer) {
|
||||
int worldId = buffer.get() & 0xFF;
|
||||
if (buffer.remaining() < 2) {
|
||||
queueBuffer(worldId);
|
||||
return;
|
||||
}
|
||||
int revision = buffer.getInt();
|
||||
int country = buffer.get() & 0xFF;
|
||||
boolean members = buffer.get() == 1;
|
||||
boolean pvp = buffer.get() == 1;
|
||||
boolean quickChat = buffer.get() == 1;
|
||||
boolean lootshare = buffer.get() == 1;
|
||||
String activity = ByteBufferUtils.getString(buffer);
|
||||
System.out.println("["+ revision + "], country = " + country + ", members = " + members + ", pvp = " + pvp + ", quickChat = " + quickChat + ", lootShare = " + lootshare + ", activity = " + activity);
|
||||
for (int i = 0; i < CHECK.length(); i++) {
|
||||
if ((char) buffer.get() != CHECK.charAt(i)) {
|
||||
session.write(3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (worldId >= ServerConstants.WORLD_LIMIT) {
|
||||
session.write(0);
|
||||
return;
|
||||
}
|
||||
if (WorldDatabase.isActive(worldId)) {
|
||||
session.write(2);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
WorldInfo info = new WorldInfo(worldId, session.getAddress(), revision, CountryFlag.values()[country], activity, members, pvp, quickChat, lootshare);
|
||||
WorldDatabase.register(info).configure(session);
|
||||
session.write(1);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
session.write(3);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package ms.net.event;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import ms.net.EventProducer;
|
||||
import ms.net.IoSession;
|
||||
import ms.net.IoWriteEvent;
|
||||
import ms.net.producer.PacketEventProducer;
|
||||
|
||||
/**
|
||||
* Handles game world registry writing events.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public final class RegistryWriteEvent extends IoWriteEvent {
|
||||
|
||||
/**
|
||||
* The event producer.
|
||||
*/
|
||||
public static final EventProducer PRODUCER = new PacketEventProducer();
|
||||
|
||||
/**
|
||||
* Constructs a new {@code RegistryWriteEvent} {@code Object}.
|
||||
* @param session The I/O session.
|
||||
* @param context The writing context.
|
||||
*/
|
||||
public RegistryWriteEvent(IoSession session, Object context) {
|
||||
super(session, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(IoSession session, Object context) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1);
|
||||
int opcode = (int) context;
|
||||
buffer.put((byte) opcode);
|
||||
buffer.flip();
|
||||
if (opcode == 1) {
|
||||
session.setProducer(PRODUCER);
|
||||
}
|
||||
session.queue(buffer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,677 @@
|
||||
package ms.net.packet;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import ms.system.util.ByteBufferUtils;
|
||||
|
||||
/**
|
||||
* Represents the buffer used for reading/writing packets.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public class IoBuffer {
|
||||
|
||||
/**
|
||||
* The bit masks.
|
||||
*/
|
||||
private static final int[] BIT_MASK = new int[32];
|
||||
|
||||
/**
|
||||
* The packet size.
|
||||
*/
|
||||
private int packetSize;
|
||||
|
||||
/**
|
||||
* The opcode.
|
||||
*/
|
||||
private final int opcode;
|
||||
|
||||
/**
|
||||
* The packet header.
|
||||
*/
|
||||
private final PacketHeader header;
|
||||
|
||||
/**
|
||||
* The byte buffer.
|
||||
*/
|
||||
private ByteBuffer buf;
|
||||
|
||||
/**
|
||||
* The bit position.
|
||||
*/
|
||||
private int bitPosition = 0;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code IoBuffer} {@code Object}.
|
||||
*/
|
||||
public IoBuffer() {
|
||||
this(-1, PacketHeader.NORMAL, ByteBuffer.allocate(2048));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code IoBuffer} {@code Object}.
|
||||
* @param opcode The opcode.
|
||||
*/
|
||||
public IoBuffer(int opcode) {
|
||||
this(opcode, PacketHeader.NORMAL, ByteBuffer.allocate(2048));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code IoBuffer} {@code Object}.
|
||||
* @param opcode The opcode.
|
||||
* @param header The packet header.
|
||||
*/
|
||||
public IoBuffer(int opcode, PacketHeader header) {
|
||||
this(opcode, header, ByteBuffer.allocate((1 << 16) + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code IoBuffer} {@code Object}.
|
||||
* @param opcode The opcode.
|
||||
* @param header The packet header.
|
||||
* @param buf The byte buffer.
|
||||
*/
|
||||
public IoBuffer(int opcode, PacketHeader header, ByteBuffer buf) {
|
||||
this.opcode = opcode;
|
||||
this.header = header;
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
static {
|
||||
for (int i = 0; i < 32; i++) {
|
||||
BIT_MASK[i] = (1 << i) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer clear() {
|
||||
buf.clear();
|
||||
bitPosition = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer put(int val) {
|
||||
buf.put((byte) val);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param datas
|
||||
* @param offset
|
||||
* @param len
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putBytes(byte[] datas, int offset, int len) {
|
||||
for (int i = offset; i < len; i++) {
|
||||
put(datas[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public final void getBytes(byte data[], int off, int len) {
|
||||
for (int k = off; k < len + off; k++) {
|
||||
data[k] = data[off++];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putA(int val) {
|
||||
buf.put((byte) (val + 128));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putC(int val) {
|
||||
buf.put((byte) -val);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putS(int val) {
|
||||
buf.put((byte) (128 - val));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putTri(int val) {
|
||||
buf.put((byte) (val >> 16));
|
||||
buf.put((byte) (val >> 8));
|
||||
buf.put((byte) val);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putShort(int val) {
|
||||
buf.putShort((short) val);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putLEShort(int val) {
|
||||
buf.put((byte) val);
|
||||
buf.put((byte) (val >> 8));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putShortA(int val) {
|
||||
buf.put((byte) (val >> 8));
|
||||
buf.put((byte) (val + 128));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putLEShortA(int val) {
|
||||
buf.put((byte) (val + 128));
|
||||
buf.put((byte) (val >> 8));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putInt(int val) {
|
||||
buf.putInt(val);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putLEInt(int val) {
|
||||
buf.put((byte) val);
|
||||
buf.put((byte) (val >> 8));
|
||||
buf.put((byte) (val >> 16));
|
||||
buf.put((byte) (val >> 24));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putIntA(int val) {
|
||||
buf.put((byte) (val >> 8));
|
||||
buf.put((byte) val);
|
||||
buf.put((byte) (val >> 24));
|
||||
buf.put((byte) (val >> 16));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putIntB(int val) {
|
||||
buf.put((byte) (val >> 16));
|
||||
buf.put((byte) (val >> 24));
|
||||
buf.put((byte) val);
|
||||
buf.put((byte) (val >> 8));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putLong(long val) {
|
||||
buf.putLong(val);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putSmart(int val) {
|
||||
if (val > Byte.MAX_VALUE) {
|
||||
buf.putShort((short) (val + 32768));
|
||||
} else {
|
||||
buf.put((byte) val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putIntSmart(int val) {
|
||||
if (val > Short.MAX_VALUE) {
|
||||
buf.putInt(val + 32768);
|
||||
} else {
|
||||
buf.putShort((short) val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putString(String val) {
|
||||
buf.put(val.getBytes());
|
||||
buf.put((byte) 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putJagString(String val) {
|
||||
buf.put((byte) 0);
|
||||
buf.put(val.getBytes());
|
||||
buf.put((byte) 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putJagString2(String val) {
|
||||
byte[] packed = new byte[256];
|
||||
int length = ByteBufferUtils.packGJString2(0, packed, val);
|
||||
buf.put((byte) 0).put(packed, 0, length).put((byte) 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param val
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer put(byte[] val) {
|
||||
buf.put(val);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a byte array as byte A in reverse.
|
||||
* @param data The data to put.
|
||||
* @param start The start index.
|
||||
* @param offset The offset.
|
||||
*/
|
||||
public void putReverseA(byte[] data, int start, int offset) {
|
||||
for (int i = offset + start; i >= start; i--) {
|
||||
putA(data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param numBits
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putBits(int numBits, int value) {
|
||||
int bytePos = getBitPosition() >> 3;
|
||||
int bitOffset = 8 - (getBitPosition() & 7);
|
||||
bitPosition += numBits;
|
||||
for (; numBits > bitOffset; bitOffset = 8) {
|
||||
byte b = buf.get(bytePos);
|
||||
buf.put(bytePos, b &= ~BIT_MASK[bitOffset]);
|
||||
buf.put(bytePos++, b |= value >> numBits - bitOffset & BIT_MASK[bitOffset]);
|
||||
numBits -= bitOffset;
|
||||
}
|
||||
byte b = buf.get(bytePos);
|
||||
if (numBits == bitOffset) {
|
||||
buf.put(bytePos, b &= ~BIT_MASK[bitOffset]);
|
||||
buf.put(bytePos, b |= value & BIT_MASK[bitOffset]);
|
||||
} else {
|
||||
buf.put(bytePos, b &= ~(BIT_MASK[numBits] << bitOffset - numBits));
|
||||
buf.put(bytePos, b |= (value & BIT_MASK[numBits]) << bitOffset - numBits);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param buffer
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer put(IoBuffer buffer) {
|
||||
buffer.toByteBuffer().flip();
|
||||
buf.put(buffer.toByteBuffer());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param buffer
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer putA(IoBuffer buffer) {
|
||||
buffer.toByteBuffer().flip();
|
||||
while (buffer.toByteBuffer().hasRemaining()) {
|
||||
putA(buffer.toByteBuffer().get());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param buffer
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer put(ByteBuffer buffer) {
|
||||
buf.put(buffer);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer setBitAccess() {
|
||||
bitPosition = buf.position() * 8;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer setByteAccess() {
|
||||
buf.position((getBitPosition() + 7) / 8);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int get() {
|
||||
return buf.get();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getA() {
|
||||
return (buf.get() & 0xFF) - 128;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getC() {
|
||||
return -buf.get();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getS() {
|
||||
return 128 - buf.get();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getTri() {
|
||||
return ((buf.get() << 16) & 0xFF) | ((buf.get() << 8) & 0xFF) | (buf.get() & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getShort() {
|
||||
return buf.getShort();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getLEShort() {
|
||||
return (buf.get() & 0xFF) | ((buf.get() & 0xFF) << 8);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getShortA() {
|
||||
return ((buf.get() & 0xFF) << 8) | (buf.get() - 128 & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getLEShortA() {
|
||||
return (buf.get() - 128 & 0xFF) | ((buf.get() & 0xFF) << 8);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getInt() {
|
||||
return buf.getInt();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getLEInt() {
|
||||
return (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8)
|
||||
+ ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getIntA() {
|
||||
return ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF)+ ((buf.get() & 0xFF) << 24) + ((buf.get() & 0xFF) << 16);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getIntB() {
|
||||
return ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24)
|
||||
+ (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getLongL() {
|
||||
long first = getIntB();
|
||||
long second = getIntB();
|
||||
if (second < 0)
|
||||
second = second & 0xffffffffL;
|
||||
return (first << -41780448) + second;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public long getLong() {
|
||||
return buf.getLong();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getSmart() {
|
||||
int peek = buf.get(buf.position());
|
||||
if (peek <= Byte.MAX_VALUE) {
|
||||
return buf.get() & 0xFF;
|
||||
}
|
||||
return (buf.getShort() & 0xFFFF) - 32768;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getIntSmart() {
|
||||
int peek = buf.getShort(buf.position());
|
||||
if (peek <= Short.MAX_VALUE) {
|
||||
return buf.getShort() & 0xFFFF;
|
||||
}
|
||||
return (buf.getInt() & 0xFFFFFFFF) - 32768;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getString() {
|
||||
return ByteBufferUtils.getString(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getJagString() {
|
||||
buf.get();
|
||||
return ByteBufferUtils.getString(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param is
|
||||
* @param offset
|
||||
* @param length
|
||||
* @return
|
||||
*/
|
||||
public IoBuffer getReverseA(byte[] is, int offset, int length) {
|
||||
for (int i = (offset + length - 1); i >= offset; i--) {
|
||||
is[i] = (byte) (buf.get() - 128);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ByteBuffer toByteBuffer() {
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int opcode() {
|
||||
return opcode;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int readableBytes() {
|
||||
return buf.capacity() - buf.remaining();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public PacketHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public byte[] array() {
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the packetSize.
|
||||
*/
|
||||
public int getPacketSize() {
|
||||
return packetSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param packetSize the packetSize to set.
|
||||
*/
|
||||
public void setPacketSize(int packetSize) {
|
||||
this.packetSize = packetSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bitPosition.
|
||||
* @return The bitPosition.
|
||||
*/
|
||||
public int getBitPosition() {
|
||||
return bitPosition;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package ms.net.packet;
|
||||
|
||||
/**
|
||||
* Represents the types of packet headers.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public enum PacketHeader {
|
||||
|
||||
/**
|
||||
* The normal packet header.
|
||||
*/
|
||||
NORMAL,
|
||||
|
||||
/**
|
||||
* The byte packet header.
|
||||
*/
|
||||
BYTE,
|
||||
|
||||
/**
|
||||
* The short packet header.
|
||||
*/
|
||||
SHORT;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,587 @@
|
||||
package ms.net.packet;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import ms.ServerConstants;
|
||||
import ms.net.IoSession;
|
||||
import ms.system.PunishmentStorage;
|
||||
import ms.world.GameServer;
|
||||
import ms.world.PlayerSession;
|
||||
import ms.world.WorldDatabase;
|
||||
import ms.system.communication.ClanRank;
|
||||
import ms.system.communication.ClanRepository;
|
||||
import ms.system.communication.CommunicationInfo;
|
||||
import ms.world.info.Response;
|
||||
import ms.world.info.UIDInfo;
|
||||
|
||||
/**
|
||||
* Repository class for world packets.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public final class WorldPacketRepository {
|
||||
|
||||
/**
|
||||
* Sends the player registry response.
|
||||
* @param server The game server.
|
||||
* @param player The player session.
|
||||
* @param response The registry response.
|
||||
*/
|
||||
public static void sendRegistryResponse(GameServer server, PlayerSession player, Response response) {
|
||||
IoBuffer buffer = new IoBuffer(0, PacketHeader.BYTE);
|
||||
buffer.putString(player.getUsername());
|
||||
buffer.put((byte) response.opcode());
|
||||
if (response == Response.MOVING_WORLD) {
|
||||
long delay = ServerConstants.WORLD_SWITCH_DELAY - (System.currentTimeMillis() - player.getDisconnectionTime());
|
||||
buffer.put((byte) (delay / 1000));
|
||||
}
|
||||
server.getSession().write(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the player.
|
||||
* @param player The player.
|
||||
* @param message The message to send.
|
||||
*/
|
||||
public static void sendPlayerMessage(PlayerSession player, String message) {
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
IoBuffer buffer = new IoBuffer(2, PacketHeader.BYTE);
|
||||
buffer.putString(player.getUsername());
|
||||
buffer.putString(message);
|
||||
player.getWorld().getSession().write(buffer);
|
||||
}
|
||||
|
||||
public static void sendPlayerMessage(PlayerSession player, String[] messages) {
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
for (String message : messages)
|
||||
sendPlayerMessage(player, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the contact information.
|
||||
* @param player The player.
|
||||
*/
|
||||
public static void sendContactInformation(PlayerSession player) {
|
||||
CommunicationInfo info = player.getCommunication();
|
||||
IoBuffer buffer = new IoBuffer(3, PacketHeader.SHORT);
|
||||
buffer.putString(player.getUsername());
|
||||
buffer.put(info.getContacts().size());
|
||||
for (String contact : info.getContacts().keySet()) {
|
||||
buffer.putString(contact);
|
||||
buffer.put(info.getRank(contact).ordinal());
|
||||
buffer.put(CommunicationInfo.getWorldId(player, contact));
|
||||
}
|
||||
buffer.put(info.getBlocked().size());
|
||||
for (String contact : info.getBlocked()) {
|
||||
buffer.putString(contact);
|
||||
}
|
||||
if (info.getCurrentClan() == null) {
|
||||
buffer.put(0);
|
||||
} else {
|
||||
buffer.put(1);
|
||||
buffer.putString(info.getCurrentClan());
|
||||
}
|
||||
player.getWorld().getSession().write(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a contact update.
|
||||
* @param player The player who's contacts we're changing.
|
||||
* @param contact The contact to update.
|
||||
* @param block If we're updating the blocked list.
|
||||
* @param remove If the contact should be removed.
|
||||
* @param worldId The world id of the contact.
|
||||
* @param rank The clan rank.
|
||||
*/
|
||||
public static void sendContactUpdate(PlayerSession player, String contact, boolean block, boolean remove, int worldId, ClanRank rank) {
|
||||
IoBuffer buffer = new IoBuffer(4, PacketHeader.BYTE);
|
||||
buffer.putString(player.getUsername());
|
||||
buffer.putString(contact);
|
||||
buffer.put((byte) (block ? 1 : 0));
|
||||
if (rank != null) {
|
||||
buffer.put((byte) (2 + rank.ordinal()));
|
||||
} else {
|
||||
buffer.put((byte) (remove ? 1 : 0));
|
||||
if (!block && !remove) {
|
||||
buffer.put((byte) worldId);
|
||||
}
|
||||
}
|
||||
player.getWorld().getSession().write(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a clan message.
|
||||
* @param player The player to send the message to.
|
||||
* @param p The player sending the message.
|
||||
* @param message The message to send.
|
||||
* @param type The message type.
|
||||
*/
|
||||
public static void sendMessage(PlayerSession player, PlayerSession p, int type, String message) {
|
||||
IoBuffer buffer = new IoBuffer(5, PacketHeader.BYTE);
|
||||
buffer.putString(player.getUsername());
|
||||
buffer.putString(p.getUsername());
|
||||
buffer.put((byte) type);
|
||||
buffer.put((byte) p.getChatIcon());
|
||||
buffer.putString(message);
|
||||
player.getWorld().getSession().write(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends clan information to the server.
|
||||
* @param server The server.
|
||||
* @param clan The clan.
|
||||
*/
|
||||
public static void sendClanInformation(GameServer server, ClanRepository clan) {
|
||||
IoBuffer buffer = new IoBuffer(6, PacketHeader.SHORT);
|
||||
buffer.putString(clan.getOwner().getUsername());
|
||||
buffer.putString(clan.getName());
|
||||
int length = clan.getPlayers().size();
|
||||
if (length > ClanRepository.MAX_MEMBERS) {
|
||||
length = ClanRepository.MAX_MEMBERS;
|
||||
}
|
||||
buffer.put((byte) length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
PlayerSession player = clan.getPlayers().get(i);
|
||||
buffer.putString(player.getUsername());
|
||||
buffer.put(player.getWorldId());
|
||||
buffer.put((byte) clan.getRank(player).ordinal());
|
||||
}
|
||||
buffer.put((byte) clan.getJoinRequirement().ordinal());
|
||||
buffer.put((byte) clan.getKickRequirement().ordinal());
|
||||
buffer.put((byte) clan.getMessageRequirement().ordinal());
|
||||
buffer.put((byte) clan.getLootRequirement().ordinal());
|
||||
server.getSession().write(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the leave clan packet.
|
||||
* @param player The player leaving the clan.
|
||||
*/
|
||||
public static void sendLeaveClan(PlayerSession player) {
|
||||
IoBuffer buffer = new IoBuffer(7, PacketHeader.BYTE);
|
||||
buffer.putString(player.getUsername());
|
||||
player.getWorld().getSession().write(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the player login notification.
|
||||
* @param server The server.
|
||||
* @param player The player logging in.
|
||||
* @param names The names.
|
||||
*/
|
||||
public static void notifyPlayers(GameServer server, PlayerSession player, List<String> names) {
|
||||
IoBuffer buffer = new IoBuffer(8, PacketHeader.SHORT);
|
||||
buffer.putString(player.getUsername());
|
||||
buffer.put((byte) player.getWorldId());
|
||||
buffer.put((byte) names.size());
|
||||
for (String name : names) {
|
||||
buffer.putString(name);
|
||||
}
|
||||
server.getSession().write(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the game server a player logged out.
|
||||
* @param server The game server to notify.
|
||||
* @param player The player logging out.
|
||||
*/
|
||||
public static void notifyLogout(GameServer server, PlayerSession player) {
|
||||
IoBuffer buffer = new IoBuffer(9, PacketHeader.BYTE);
|
||||
buffer.putString(player.getUsername());
|
||||
server.getSession().write(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the update countdown to the server.
|
||||
* @param server The server.
|
||||
* @param ticks The amount of ticks left.
|
||||
*/
|
||||
public static void sendUpdate(GameServer server, int ticks) {
|
||||
IoBuffer buffer = new IoBuffer(10);
|
||||
buffer.putInt(ticks);
|
||||
server.getSession().write(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the punishment update packet.
|
||||
* @param world The world to send the packet to.
|
||||
* @param key The punishment key.
|
||||
* @param type The punishment type.
|
||||
* @param duration The duration of the punishment.
|
||||
*/
|
||||
public static void sendPunishUpdate(GameServer world, String key, int type, long duration) {
|
||||
IoBuffer buffer = new IoBuffer(11, PacketHeader.BYTE);
|
||||
buffer.putString(key);
|
||||
buffer.put((byte) type);
|
||||
buffer.putLong(duration);
|
||||
world.getSession().write(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a configuration reload.
|
||||
* @param world the world.
|
||||
*/
|
||||
public static void sendConfigReload(GameServer world) {
|
||||
world.getSession().write(new IoBuffer(15, PacketHeader.BYTE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming world packets.
|
||||
* @param session The I/O session.
|
||||
* @param opcode The opcode.
|
||||
* @param b The buffer to read from.
|
||||
*/
|
||||
public static void handleIncoming(IoSession session, int opcode, ByteBuffer b) {
|
||||
IoBuffer buffer = new IoBuffer(opcode, PacketHeader.NORMAL, b);
|
||||
GameServer server = session.getGameServer();
|
||||
switch (opcode) {
|
||||
case 0:
|
||||
handlePlayerRegistration(server, buffer);
|
||||
break;
|
||||
case 1:
|
||||
handlePlayerRemoval(server, buffer);
|
||||
break;
|
||||
case 2:
|
||||
handlePunishment(server, buffer);
|
||||
break;
|
||||
case 3:
|
||||
handleCommunicationRequest(server, buffer);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
handleContactUpdate(server, buffer, opcode == 5);
|
||||
break;
|
||||
case 6:
|
||||
handleJoinClan(server, buffer);
|
||||
break;
|
||||
case 7:
|
||||
handleClanRename(server, buffer);
|
||||
break;
|
||||
case 8:
|
||||
handleClanSetting(server, buffer);
|
||||
break;
|
||||
case 9:
|
||||
handleClanKick(server, buffer);
|
||||
break;
|
||||
case 10:
|
||||
handleClanMessage(server, buffer);
|
||||
break;
|
||||
case 11:
|
||||
handlePrivateMessage(server, buffer);
|
||||
break;
|
||||
case 12:
|
||||
handleClanInfoRequest(server, buffer);
|
||||
break;
|
||||
case 13:
|
||||
handleChatSetting(server, buffer);
|
||||
break;
|
||||
case 14:
|
||||
handleInfoUpdate(server, buffer);
|
||||
break;
|
||||
default:
|
||||
System.err.println("Handling incoming packet [opcode=" + opcode + ", size=" + b.limit() + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the info of a player update.
|
||||
* @param server the server.
|
||||
* @param buffer the buffer.
|
||||
*/
|
||||
private static void handleInfoUpdate(GameServer server, IoBuffer buffer) {
|
||||
String username = buffer.getString();
|
||||
PlayerSession player = server.getPlayers().get(username);
|
||||
if (player != null) {
|
||||
player.setChatIcon((int) buffer.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a player registration.
|
||||
* @param server The game server.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
private static void handlePlayerRegistration(GameServer server, IoBuffer buffer) {
|
||||
String username = buffer.getString();
|
||||
String password = buffer.getString();
|
||||
String ipAddress = buffer.getString();
|
||||
String macAddress = buffer.getString();
|
||||
String compName = buffer.getString();
|
||||
String serial = buffer.getString();
|
||||
int rights = server.getInfo().getRevision() == 498 ? 0 : buffer.getInt();
|
||||
int chatIcon = server.getInfo().getRevision() == 498 ? 0 : buffer.get();
|
||||
UIDInfo uid = new UIDInfo(ipAddress, compName, macAddress, serial);
|
||||
PlayerSession player = new PlayerSession(username, password, new UIDInfo(ipAddress, compName, macAddress, serial));
|
||||
if (WorldDatabase.isActivePlayer(username)) {
|
||||
sendRegistryResponse(server, player, Response.ALREADY_ONLINE);
|
||||
return;
|
||||
}
|
||||
player.setUid(uid);
|
||||
player.setRights(rights);
|
||||
player.setChatIcon(chatIcon);
|
||||
if (PunishmentStorage.isSystemBanned(uid)) {
|
||||
sendRegistryResponse(server, player, Response.BANNED);
|
||||
return;
|
||||
}
|
||||
player.parse();
|
||||
if (player.isBanned()) {
|
||||
sendRegistryResponse(server, player, Response.ACCOUNT_DISABLED);
|
||||
return;
|
||||
}
|
||||
if (player.getLastWorld() != server.getInfo().getWorldId() && player.hasMovedWorld()) {
|
||||
sendRegistryResponse(server, player, Response.MOVING_WORLD);
|
||||
return;
|
||||
}
|
||||
server.register(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the removal of a player.
|
||||
* @param server The game server.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
private static void handlePlayerRemoval(GameServer server, IoBuffer buffer) {
|
||||
String username = buffer.getString();
|
||||
PlayerSession session = server.getPlayers().get(username);
|
||||
if (session != null) {
|
||||
session.setActive(false);
|
||||
PlayerSession player = server.getPlayers().remove(username);
|
||||
if (player != null) {
|
||||
session.remove();
|
||||
}
|
||||
session.setWorldId(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a player registration.
|
||||
* @param server The game server.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
private static void handlePunishment(GameServer server, IoBuffer buffer) {
|
||||
int type = buffer.get() & 0xFF;
|
||||
String target = buffer.getString();
|
||||
long duration = buffer.getLong();
|
||||
String staff = buffer.getString();
|
||||
PunishmentStorage.handlePunishment(staff, target, type, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the communication info request packet.
|
||||
* @param server The game server.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
private static void handleCommunicationRequest(GameServer server, IoBuffer buffer) {
|
||||
String username = buffer.getString();
|
||||
PlayerSession player = server.getPlayers().get(username);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
sendContactInformation(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a contact update packet.
|
||||
* @param server The server.
|
||||
* @param buffer The buffer to read from.
|
||||
* @param block If the list is for blocked players.
|
||||
*/
|
||||
private static void handleContactUpdate(GameServer server, IoBuffer buffer, boolean block) {
|
||||
String username = buffer.getString();
|
||||
PlayerSession player = server.getPlayers().get(username);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
String contact = buffer.getString();
|
||||
switch (buffer.get()) {
|
||||
case 0:
|
||||
if (block) {
|
||||
player.getCommunication().block(contact);
|
||||
break;
|
||||
}
|
||||
player.getCommunication().add(contact);
|
||||
break;
|
||||
case 1:
|
||||
player.getCommunication().remove(contact, block);
|
||||
break;
|
||||
case 2:
|
||||
player.getCommunication().updateClanRank(contact, ClanRank.values()[buffer.get()]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a clan related packet.
|
||||
* @param server The game server.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
private static void handleJoinClan(GameServer server, IoBuffer buffer) {
|
||||
String name = buffer.getString();
|
||||
String clanName = buffer.getString();
|
||||
PlayerSession player = server.getPlayers().get(name);
|
||||
if (player == null || !player.isActive()) {
|
||||
System.err.println("Invalid player specified in clan packet!");
|
||||
return;
|
||||
}
|
||||
if (player.getClan() != null) {
|
||||
player.getClan().leave(player, true);
|
||||
return;
|
||||
}
|
||||
if (clanName.length() < 1) {
|
||||
sendLeaveClan(player);
|
||||
return;
|
||||
}
|
||||
ClanRepository clan = ClanRepository.get(server, clanName);
|
||||
if (clan == null) {
|
||||
sendPlayerMessage(player, new String[]{ "The channel you tried to join does not exist.", "Try joining the main clan named '" + ServerConstants.SERVER_NAME + "'.:clan:" });
|
||||
return;
|
||||
}
|
||||
clan.enter(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles renaming a clan.
|
||||
* @param server The server.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
private static void handleClanRename(GameServer server, IoBuffer buffer) {
|
||||
String username = buffer.getString();
|
||||
String name = buffer.getString();
|
||||
PlayerSession player = server.getPlayers().get(username);
|
||||
if (player == null || !player.isActive()) {
|
||||
return;
|
||||
}
|
||||
ClanRepository clan = ClanRepository.getClans().get(username);
|
||||
player.getCommunication().setClanName(name);
|
||||
if (clan != null) {
|
||||
if (name.length() < 1) {
|
||||
clan.clean(true);
|
||||
} else {
|
||||
clan.rename(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changing clan settings packet.
|
||||
* @param server The game server.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
private static void handleClanSetting(GameServer server, IoBuffer buffer) {
|
||||
String username = buffer.getString();
|
||||
int type = buffer.get();
|
||||
ClanRank rank = type < 4 ? ClanRank.values()[buffer.get() & 0xFF] : null;
|
||||
PlayerSession player = server.getPlayers().get(username);
|
||||
if (player == null || !player.isActive()) {
|
||||
return;
|
||||
}
|
||||
ClanRepository clan = ClanRepository.get(server,username);
|
||||
switch (type) {
|
||||
case 0:
|
||||
player.getCommunication().setJoinRequirement(rank);
|
||||
if (clan != null) {
|
||||
clan.clean(false);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
player.getCommunication().setMessageRequirement(rank);
|
||||
break;
|
||||
case 2:
|
||||
player.getCommunication().setKickRequirement(rank);
|
||||
if (clan != null) {
|
||||
clan.update();
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
player.getCommunication().setLootRequirement(rank);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles kicking a player from the clan.
|
||||
* @param server The game server.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
private static void handleClanKick(GameServer server, IoBuffer buffer) {
|
||||
String username = buffer.getString();
|
||||
String playerName = buffer.getString();
|
||||
PlayerSession player = server.getPlayers().get(username);
|
||||
if (player == null || !player.isActive() || player.getClan() == null) {
|
||||
return;
|
||||
}
|
||||
PlayerSession target = WorldDatabase.getPlayer(playerName);
|
||||
if (target == null || !target.isActive()) {
|
||||
return;
|
||||
}
|
||||
player.getClan().kick(player, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a clan message.
|
||||
* @param server The game server.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
private static void handleClanMessage(GameServer server, IoBuffer buffer) {
|
||||
String username = buffer.getString();
|
||||
String message = buffer.getString();
|
||||
PlayerSession player = server.getPlayers().get(username);
|
||||
if (player == null || !player.isActive() || player.getClan() == null) {
|
||||
return;
|
||||
}
|
||||
player.getClan().message(player, message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles a clan message.
|
||||
* @param server The game server.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
private static void handlePrivateMessage(GameServer server, IoBuffer buffer) {
|
||||
String username = buffer.getString();
|
||||
String receiver = buffer.getString();
|
||||
String message = buffer.getString();
|
||||
PlayerSession player = server.getPlayers().get(username);
|
||||
if (player == null || !player.isActive()) {
|
||||
return;
|
||||
}
|
||||
player.getCommunication().sendMessage(receiver, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a clan information request packet.
|
||||
* @param server The server.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
private static void handleClanInfoRequest(GameServer server, IoBuffer buffer) {
|
||||
String name = buffer.getString();
|
||||
ClanRepository clan = ClanRepository.get(server, name);
|
||||
if (clan == null) {
|
||||
return;
|
||||
}
|
||||
sendClanInformation(server, clan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a chat setting update packet.
|
||||
* @param server The server.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
private static void handleChatSetting(GameServer server, IoBuffer buffer) {
|
||||
String name = buffer.getString();
|
||||
int publicSetting = buffer.get();
|
||||
int privateSetting = buffer.get();
|
||||
int tradeSetting = buffer.get();
|
||||
PlayerSession player = server.getPlayers().get(name);
|
||||
if (player == null || !player.isActive()) {
|
||||
return;
|
||||
}
|
||||
player.getCommunication().updateSettings(publicSetting, privateSetting, tradeSetting);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package ms.net.producer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import ms.net.EventProducer;
|
||||
import ms.net.IoReadEvent;
|
||||
import ms.net.IoSession;
|
||||
import ms.net.IoWriteEvent;
|
||||
import ms.net.event.HSReadEvent;
|
||||
import ms.net.event.HSWriteEvent;
|
||||
|
||||
/**
|
||||
* Produces I/O events for the handshake protocol.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public final class HSEventProducer implements EventProducer {
|
||||
|
||||
@Override
|
||||
public IoReadEvent produceReader(IoSession session, ByteBuffer buffer) {
|
||||
return new HSReadEvent(session, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoWriteEvent produceWriter(IoSession session, Object context) {
|
||||
return new HSWriteEvent(session, context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package ms.net.producer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import ms.net.EventProducer;
|
||||
import ms.net.IoReadEvent;
|
||||
import ms.net.IoSession;
|
||||
import ms.net.IoWriteEvent;
|
||||
import ms.net.event.PacketReadEvent;
|
||||
import ms.net.event.PacketWriteEvent;
|
||||
|
||||
/**
|
||||
* The packet event producer.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public final class PacketEventProducer implements EventProducer {
|
||||
|
||||
@Override
|
||||
public IoReadEvent produceReader(IoSession session, ByteBuffer buffer) {
|
||||
return new PacketReadEvent(session, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoWriteEvent produceWriter(IoSession session, Object context) {
|
||||
return new PacketWriteEvent(session, context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package ms.net.producer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import ms.net.EventProducer;
|
||||
import ms.net.IoReadEvent;
|
||||
import ms.net.IoSession;
|
||||
import ms.net.IoWriteEvent;
|
||||
import ms.net.event.RegistryReadEvent;
|
||||
import ms.net.event.RegistryWriteEvent;
|
||||
|
||||
/**
|
||||
* Handles world server registry.
|
||||
* @author Emperor
|
||||
*
|
||||
*/
|
||||
public final class RegistryEventProducer implements EventProducer {
|
||||
|
||||
@Override
|
||||
public IoReadEvent produceReader(IoSession session, ByteBuffer buffer) {
|
||||
return new RegistryReadEvent(session, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IoWriteEvent produceWriter(IoSession session, Object context) {
|
||||
return new RegistryWriteEvent(session, context);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user