Repository reorganisation

Unified kotlin and java into just src/main
Unified the rs09 and core packages
Took all content out of the core package, and placed it into the new content package
Reorganised all source code relating to content to be easier to find and explore
This commit is contained in:
Ceikry
2023-01-26 13:59:28 +00:00
committed by Ryan
parent e4b799c44a
commit a100affda2
3156 changed files with 24205 additions and 30585 deletions
+30
View File
@@ -0,0 +1,30 @@
package core.net;
/**
* Network constants.
* @author Vexia
*/
public final class Constants {
/**
* Represents the post the server is hosted on.
*/
public static final int PORT = 43594;
/**
* The revision of the game server.
*/
public static final int REVISION = 530;
/**
* The client build of the client, used for notifying players of a client
* update.
*/
public static final int CLIENT_BUILD = 1;
/**
* The default Management server IP.
*/
public static final String DEFAULT_MS_IP = "127.0.0.1";
}
@@ -0,0 +1,27 @@
package core.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,113 @@
package core.net;
import core.game.world.repository.Repository;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.ExecutorService;
/**
* I/O event handling.
* @author Emperor
*/
public class IoEventHandler {
/**
* The executor service.
*/
protected 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.
* @throws IOException When an I/O exception occurs.
*/
public void connect(SelectionKey key) throws IOException {
/*
* 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();
try {
if (channel.read(buffer) == -1) {
if (session != null && session.getPlayer() != null) {
Repository.getDisconnectionQueue().add(session.getPlayer());
}
key.cancel();
return;
}
} catch (IOException e) {
if (e.getMessage().contains("reset by peer")) {
session.disconnect();
if (session.getPlayer() != null)
session.getPlayer().clear(true);
} else e.printStackTrace();
}
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();
}
}
}
+72
View File
@@ -0,0 +1,72 @@
package core.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);
}
+443
View File
@@ -0,0 +1,443 @@
package core.net;
import core.cache.crypto.ISAACPair;
import core.game.node.entity.player.Player;
import core.game.node.entity.player.info.ClientInfo;
import core.game.system.task.Pulse;
import core.auth.AuthResponse;
import core.game.world.GameWorld;
import core.net.producer.HSEventProducer;
import core.net.producer.LoginEventProducer;
import core.game.world.repository.Repository;
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;
/**
* 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;
public int isaacInputOpcode = 0;
/**
* 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<>(20);
/**
* The currently queued reading data.
*/
private ByteBuffer readingQueue;
/**
* The writing lock.
*/
private Lock writingLock = new ReentrantLock();
/**
* The ISAAC cipher pair.
*/
private ISAACPair isaacPair;
/**
* The name hash.
*/
private int nameHash;
/**
* The server key.
*/
private long serverKey;
/**
* The JS-5 encryption value.
*/
private int js5Encryption;
/**
* The object.
*/
private Object object;
/**
* If the session is active.
*/
private boolean active = true;
/**
* The last ping time stamp.
*/
private long lastPing;
/**
* The address.
*/
private final String address;
/**
* The JS-5 queue.
*/
private final JS5Queue js5Queue;
/**
* The client info.
*/
private ClientInfo clientInfo;
public String associatedUsername;
/**
* 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;
this.address = getRemoteAddress().replaceAll("/", "").split(":")[0];
this.js5Queue = new JS5Queue(this);
}
/**
* 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 (!(context instanceof AuthResponse) && producer instanceof LoginEventProducer) {
// new Throwable().printStackTrace();
return;
}
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){
e.printStackTrace();
writingLock.unlock();
}
writingQueue.add(buffer);
writingLock.unlock();
write();
}
/**
* Handles the writing of all buffers in the queue.
*/
public void write() {
if (!key.isValid()) {
disconnect();
return;
}
try {
writingLock.tryLock(1000L, TimeUnit.MILLISECONDS);
} catch (Exception e){
e.printStackTrace();
writingLock.unlock();
return;
}
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();
if (object instanceof Player) {
final Player p = getPlayer();
GameWorld.getPulser().submit(new Pulse(0) {
@Override
public boolean pulse() {
if (p.isActive() && !p.getSession().active) {
p.clear();
}
return true;
}
});
}
object = null;
} 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;
}
/**
* @return The player.
*/
public Player getPlayer() {
if (object == null) {
object = Repository.getPlayerByName(associatedUsername);
}
return object instanceof Player ? ((Player) object) : null;
}
/**
* Gets the object.
* @return the object.
*/
public Object getObject() {
return object;
}
/**
* @param object The object to set.
*/
public void setObject(Object object) {
this.object = object;
}
/**
* 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 isaacPair.
* @return The isaacPair.
*/
public ISAACPair getIsaacPair() {
return isaacPair;
}
/**
* Sets the isaacPair.
* @param isaacPair The isaacPair to set.
*/
public void setIsaacPair(ISAACPair isaacPair) {
this.isaacPair = isaacPair;
}
/**
* Gets the js5Queue.
* @return the js5Queue
*/
public JS5Queue getJs5Queue() {
return js5Queue;
}
/**
* Gets the clientInfo value.
* @return The clientInfo.
*/
public ClientInfo getClientInfo() {
return clientInfo;
}
/**
* Sets the clientInfo value.
* @param clientInfo The clientInfo to set.
*/
public void setClientInfo(ClientInfo clientInfo) {
this.clientInfo = clientInfo;
}
}
@@ -0,0 +1,50 @@
package core.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);
}
+97
View File
@@ -0,0 +1,97 @@
package core.net;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Handles the JS5 queue for a session.
* @author Techdaan
*/
public final class JS5Queue {
public static AtomicBoolean RUNNING = new AtomicBoolean(true);
private static final Js5QueueHandler handler = new Js5QueueHandler();
private static class Js5QueueHandler extends Thread {
private final LinkedBlockingDeque<Js5Request> requests = new LinkedBlockingDeque<>();
@Override
public void run() {
while (RUNNING.get()) {
try {
Js5Request request = requests.take();
JS5Queue queue = request.queue;
if (queue.session.isActive()) {
queue.session.write(new int[]{request.index, request.archive, request.priority ? 1 : 0});
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
static {
try {
handler.start();
} catch (Exception e) {
e.printStackTrace();
}
}
private static class Js5Request {
private final JS5Queue queue;
private final int index;
private final int archive;
private final boolean priority;
public Js5Request(JS5Queue queue, int index, int archive, boolean priority) {
this.queue = queue;
this.index = index;
this.archive = archive;
this.priority = priority;
}
}
/**
* The I/O session.
*/
private final IoSession session;
/**
* Constructs a new {@code JS5Queue} {@code Object}.
* @param session The I/O session.
*/
public JS5Queue(IoSession session) {
this.session = session;
}
/**
* Queues a JS-5 request.
* Timers:
* 1000ms queue up files for this long
* 100ms release a file from the queue
*
* @param container The container.
* @param archive The archive.
* @param highPriority If the request is high priority.
*
*/
public void queue(int container, int archive, boolean highPriority) {
Js5Request request = new Js5Request(this, container, archive, highPriority);
handler.requests.add(request);
}
/**
* Gets the session.
* @return the session
*/
public IoSession getSession() {
return session;
}
}
+148
View File
@@ -0,0 +1,148 @@
package core.net;
import core.net.amsc.MSEventHandler;
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.nio.channels.SocketChannel;
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 socket channel.
*/
private ServerSocketConnection channel;
/**
* The I/O event handling instance.
*/
private IoEventHandler eventHandler;
/**
* If the reactor is running.
*/
private boolean running;
/**
* Constructs a new {@code NioReactor}.
*/
private NioReactor(IoEventHandler eventHandler) {
this.service = Executors.newSingleThreadScheduledExecutor();
this.eventHandler = eventHandler;
}
/**
* 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(new IoEventHandler(Executors.newFixedThreadPool(poolSize)));
ServerSocketChannel channel = ServerSocketChannel.open();
Selector selector = Selector.open();
channel.bind(new InetSocketAddress(port));
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_ACCEPT);
reactor.channel = new ServerSocketConnection(selector, channel);
return reactor;
}
/**
* Starts a NIO reactor used for client connections.
* @param address The IP-address to connect to.
* @param port The port used.
* @return The NIO reactor object.
* @throws IOException When an exception occurs.
*/
public static NioReactor connect(String address, int port) throws IOException {
NioReactor reactor = new NioReactor(new MSEventHandler());
Selector selector = Selector.open();
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.socket().setKeepAlive(true);
channel.socket().setTcpNoDelay(true);
channel.connect(new InetSocketAddress(address, port));
channel.register(selector, SelectionKey.OP_CONNECT);
reactor.channel = new ServerSocketConnection(selector, channel);
return reactor;
}
/**
* Starts the reactor.
*/
public void start() {
running = true;
service.execute(this);
}
@Override
public void run() {
Thread.currentThread().setName("NioReactor");
while (running) {
try {
channel.getSelector().select();
} catch (IOException e) {
e.printStackTrace();
}
Iterator<SelectionKey> iterator = channel.getSelector().selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
if (!key.isValid() || !key.channel().isOpen()) {
key.cancel();
continue;
}
if (key.isConnectable()) {
eventHandler.connect(key);
}
if (key.isAcceptable()) {
eventHandler.accept(key, channel.getSelector());
}
if (key.isReadable()) {
eventHandler.read(key);
} else if (key.isWritable()) {
eventHandler.write(key);
}
} catch (Throwable t) {
eventHandler.disconnect(key, t);
}
}
}
}
/**
* Terminates the reactor (once it's done processing current I/O events).
*/
public void terminate() {
running = false;
}
}
@@ -0,0 +1,83 @@
package core.net;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
/**
* Represents a server socket and its selector.
* @author Emperor
*/
public final class ServerSocketConnection {
/**
* The selector.
*/
private final Selector selector;
/**
* The channel.
*/
private final ServerSocketChannel channel;
/**
* The socket channel.
*/
private final SocketChannel socket;
/**
* Constructs a new {@code ServerSocketConnection} {@code Object}.
* @param selector The selector.
* @param channel The channel.
*/
public ServerSocketConnection(Selector selector, ServerSocketChannel channel) {
this.selector = selector;
this.channel = channel;
this.socket = null;
}
/**
* Constructs a new {@Code ServerSocketConnection} {@Code
* Object}
* @param selector The selector.
* @param channel The channel.
*/
public ServerSocketConnection(Selector selector, SocketChannel channel) {
this.selector = selector;
this.socket = channel;
this.channel = null;
}
/**
* If the channel is used as client.
* @return {@code True} if so.
*/
public boolean isClient() {
return socket != null;
}
/**
* Gets the selector.
* @return the selector.
*/
public Selector getSelector() {
return selector;
}
/**
* Gets the channel.
* @return the channel.
*/
public ServerSocketChannel getChannel() {
return channel;
}
/**
* Gets the socket.
* @return the socket
*/
public SocketChannel getSocket() {
return socket;
}
}
@@ -0,0 +1,66 @@
package core.net.amsc;
import core.tools.SystemLogger;
import core.net.IoEventHandler;
import core.net.IoSession;
import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Executors;
/**
* Handles the management server events.
* @author Emperor
*/
public final class MSEventHandler extends IoEventHandler {
/**
* Constructs a new {@Code MSEventHandler} {@Code Object}
*/
public MSEventHandler() {
super(Executors.newSingleThreadExecutor());
}
@Override
public void connect(SelectionKey key) throws IOException {
SocketChannel ch = (SocketChannel) key.channel();
try {
if (ch.finishConnect()) {
key.interestOps(key.interestOps() ^ SelectionKey.OP_CONNECT);
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
IoSession session = (IoSession) key.attachment();
key.attach(session = new IoSession(key, service));
WorldCommunicator.register(session);
return;
}
} catch (Throwable t) {
t.printStackTrace();
}
SystemLogger.logErr(this.getClass(), "Failed connecting to Management Server!");
WorldCommunicator.terminate();
}
@Override
public void accept(SelectionKey key, Selector selector) throws IOException {
super.write(key);
}
@Override
public void read(SelectionKey key) throws IOException {
super.read(key);
}
@Override
public void write(SelectionKey key) {
super.write(key);
}
@Override
public void disconnect(SelectionKey key, Throwable t) {
super.disconnect(key, t);
WorldCommunicator.terminate();
}
}
@@ -0,0 +1,97 @@
package core.net.amsc;
import core.game.node.entity.player.Player;
import core.game.system.communication.*;
import core.net.packet.IoBuffer;
import core.net.packet.PacketHeader;
/**
* The Management server packet repository.
* @author Emperor
*/
public final class MSPacketRepository {
/**
* Sends a contact update.
*
* @param username The username.
* @param contact The contact's username.
* @param remove If we're removing the contact from the list.
* @param block If the contact list is for the blocked players.
* @param rank The new clan rank (or null when not updating clan rank!).
*/
public static void sendContactUpdate(String username, String contact, boolean remove, boolean block, ClanRank rank) {
IoBuffer buffer = new IoBuffer(block ? 5 : 4, PacketHeader.BYTE);
buffer.putString(username);
buffer.putString(contact);
if (rank != null) {
buffer.put((byte) 2);
buffer.put((byte) rank.ordinal());
} else {
buffer.put((byte) (remove ? 1 : 0));
}
WorldCommunicator.getSession().write(buffer);
}
/**
* Sends the clan rename packet.
*
* @param player The player.
* @param clanName The clan name.
*/
public static void sendClanRename(Player player, String clanName) {
IoBuffer buffer = new IoBuffer(7, PacketHeader.BYTE);
buffer.putString(player.getName());
buffer.putString(clanName);
WorldCommunicator.getSession().write(buffer);
}
/**
* Sets a clan setting.
*
* @param player The player.
* @param type The setting type.
* @param rank The rank to set.
*/
public static void setClanSetting(Player player, int type, ClanRank rank) {
if (!WorldCommunicator.isEnabled()) {
return;
}
IoBuffer buffer = new IoBuffer(8, PacketHeader.BYTE);
buffer.putString(player.getName());
buffer.put((byte) type);
if (rank != null) {
buffer.put((byte) rank.ordinal());
}
WorldCommunicator.getSession().write(buffer);
}
/**
* Sends the kicking a clan member packet.
*
* @param username The player's username.
* @param name The name.
*/
public static void sendClanKick(String username, String name) {
IoBuffer buffer = new IoBuffer(9, PacketHeader.BYTE);
buffer.putString(username);
buffer.putString(name);
WorldCommunicator.getSession().write(buffer);
}
/**
* Sends the chat settings.
*
* @param player The player.
* @param publicSetting The public chat setting.
* @param privateSetting The private chat setting.
* @param tradeSetting The trade setting.
*/
public static void sendChatSetting(Player player, int publicSetting, int privateSetting, int tradeSetting) {
IoBuffer buffer = new IoBuffer(13, PacketHeader.BYTE);
buffer.putString(player.getName());
buffer.put(publicSetting);
buffer.put(privateSetting);
buffer.put(tradeSetting);
WorldCommunicator.getSession().write(buffer);
}
}
@@ -0,0 +1,59 @@
package core.net.amsc;
import core.game.node.entity.player.Player;
import core.game.world.repository.Repository;
import core.net.packet.PacketRepository;
import core.net.packet.context.ContactContext;
import core.net.packet.out.ContactPackets;
/**
* The management server state.
* @author Emperor
*/
public enum ManagementServerState {
/**
* If the management server is not available.
*/
NOT_AVAILABLE(2),
/**
* If we're still connecting to the Management server.
*/
CONNECTING(1),
/**
* If we're connected to the management server.
*/
AVAILABLE(2);
/**
* The value of this state.
*/
private final int value;
/**
* Constructs a new {@code ManagementServerState} {@code Object}
* @param value The value.
*/
private ManagementServerState(int value) {
this.value = value;
}
/**
* Called when the state gets set.
*/
public void set() {
for (Player player : Repository.getPlayers()) {
PacketRepository.send(ContactPackets.class, new ContactContext(player, ContactContext.UPDATE_STATE_TYPE));
}
}
/**
* Gets the state value.
* @return The value.
*/
public int value() {
return value;
}
}
@@ -0,0 +1,210 @@
package core.net.amsc;
import core.net.EventProducer;
import core.net.IoSession;
import core.net.NioReactor;
import core.net.producer.MSHSEventProducer;
import core.game.node.entity.player.info.login.LoginParser;
import core.tools.SystemLogger;
import core.game.world.GameWorld;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Handles world communication.
* @author Emperor
*/
public final class WorldCommunicator {
/**
* The handshake events producer.
*/
private static final EventProducer HANDSHAKE_PRODUCER = new MSHSEventProducer();
/**
* The current state.
*/
private static ManagementServerState state = ManagementServerState.CONNECTING;
/**
* The I/O session.
*/
private static IoSession session;
/**
* The world information.
*/
private static final WorldStatistics[] WORLDS = new WorldStatistics[10];
/**
* The current login attempts.
*/
private static final Map<String, LoginParser> loginAttempts = new ConcurrentHashMap<>();
/**
* The NIO reactor.
*/
private static NioReactor reactor;
/**
* Registers a new world.
* @param session The session.
*/
public static void register(IoSession session) {
WorldCommunicator.session = session;
session.setProducer(HANDSHAKE_PRODUCER);
session.write(true);
WORLDS[GameWorld.getSettings().getWorldId() - 1] = new WorldStatistics(GameWorld.getSettings().getWorldId());
session.setObject(WORLDS[GameWorld.getSettings().getWorldId() - 1]);
}
/**
* Attempts to connect to the management server.
*/
public static void connect() {
try {
SystemLogger.logInfo(WorldCommunicator.class, "Attempting to connect to management server...");
setState(ManagementServerState.CONNECTING);
/*if (isLocallyHosted()) {
SystemLogger.log("Management server is hosted on local machine!");
reactor = NioReactor.connect(GameWorld.getSettings().getMsAddress(), 5555);
} else {*/
reactor = NioReactor.connect(GameWorld.getSettings().getMsAddress(), 5555);
//}
reactor.start();
} catch (Throwable e) {
SystemLogger.logErr(WorldCommunicator.class, "Unable to connect to management server");
e.printStackTrace();
terminate();
}
}
/**
* Checks if the Management server is locally hosted.
* @return {@code True} if so.
* @throws IOException When an I/O exception occurs.
*/
private static boolean isLocallyHosted() throws IOException {
InetAddress address = InetAddress.getByName(GameWorld.getSettings().getMsAddress());
if (address.isAnyLocalAddress() || address.isLoopbackAddress()) {
return true;
}
return NetworkInterface.getByInetAddress(address) != null;
}
/**
* Terminates the world communicator.
*/
public static void terminate() {
setState(ManagementServerState.NOT_AVAILABLE);
if (reactor != null) {
reactor.terminate();
reactor = null;
}
}
/**
* Gets and removes the login attempt for the given username.
* @param username The username.
* @return The login attempt.
*/
public static LoginParser finishLoginAttempt(String username) {
return loginAttempts.remove(username);
}
/**
* Gets the local world.
* @return The world statistics of this world server.
*/
public static WorldStatistics getLocalWorld() {
return WORLDS[GameWorld.getSettings().getWorldId() - 1];
}
/**
* Gets the id of the world the player is connected to.
* @param playerName The player's name.
* @return The world id, or -1 if the player wasn't connected.
*/
public static int getWorld(String playerName) {
for (int i = 0; i < WORLDS.length; i++) {
if (WORLDS[i].getPlayers().contains(playerName)) {
return i;
}
}
return -1;
}
/**
* Gets the world statistics for the given index.
* @param id The world id.
* @return The world statistics.
*/
public static WorldStatistics getWorld(int id) {
return WORLDS[id - 1];
}
/**
* Gets the session.
* @return the session
*/
public static IoSession getSession() {
return session;
}
/**
* Checks if this world is connected to the Management server.
* @return {@code True} if so.
*/
public static boolean isEnabled() {
return state == ManagementServerState.AVAILABLE;
}
/**
* Gets the login attempts mapping.
* @return The login attempts mapping.
*/
public static Map<String, LoginParser> getLoginAttempts() {
return loginAttempts;
}
/**
* Gets the state.
* @return the state
*/
public static ManagementServerState getState() {
return state;
}
/**
* Sets the state.
* @param state the state to set.
*/
public static void setState(ManagementServerState state) {
if (WorldCommunicator.state != state) {
WorldCommunicator.state = state;
state.set();
SystemLogger.logInfo(WorldCommunicator.class, "Management server status: " + state + ".");
}
}
/**
* Gets the reactor.
* @return the reactor
*/
public static NioReactor getReactor() {
return reactor;
}
/**
* Sets the reactor.
* @param reactor the reactor to set.
*/
public static void setReactor(NioReactor reactor) {
WorldCommunicator.reactor = reactor;
}
}
@@ -0,0 +1,45 @@
package core.net.amsc;
import java.util.ArrayList;
import java.util.List;
/**
* Holds information of a certain game world.
* @author Emperor
*/
public final class WorldStatistics {
/**
* The world id.
*/
private final int id;
/**
* The list of players connected to this world.
*/
private final List<String> players = new ArrayList<>(20);
/**
* Constructs a new {@code WorldStatistics} {@Code Object}
* @param id The world id.
*/
public WorldStatistics(int id) {
this.id = id;
}
/**
* Gets the players.
* @return the players
*/
public List<String> getPlayers() {
return players;
}
/**
* Gets the id.
* @return the id
*/
public int getId() {
return id;
}
}
@@ -0,0 +1,142 @@
package core.net.event;
import core.tools.SystemLogger;
import core.net.IoReadEvent;
import core.net.IoSession;
import core.net.packet.IoBuffer;
import core.net.packet.PacketProcessor;
import core.net.packet.in.Decoders530;
import core.net.packet.in.Packet;
import java.nio.ByteBuffer;
/**
* Handles game packet reading.
* @author Emperor
*/
public final class GameReadEvent extends IoReadEvent {
/**
* The incoming packet sizes, sorted by opcode.
*/
public static final int[] PACKET_SIZES = {
-3, -3, -3, 2, 2, -3, 8, -3, -3, 6, // 0-9
4, -3, -3, -3, -3, -3, -3, 0, -3, -3, // 10-19
4, 4, 1, 4, -3, -3, -3, 16, -3, -3, // 20-29
2, -3, -3, 6, 8, -3, -3, -3, -3, -1, // 30-39
-3, -3, -3, -3, -1, -3, -3, -3, 6, -3, // 40-49
-3, -3, -3, 6, -3, 8, -3, 8, -3, -3, // 50-59
-3, -3, -3, -3, 6, -1, 6, -3, 2, -3, // 60-69
-3, 2, 2, 12, -3, 6, -3, -1, 2, 12, // 70-79
-3, 8, 12, -3, 6, 8, -3, -3, -3, -3, // 80-89
-3, -3, 2, 0, 2, -3, -3, -3, 4, 10, // 90-99
-3, 14, -3, -3, 8, -3, 2, -3, -3, 6, // 100-109
0, 2, -3, -3, 2, 10, -3, -1, -3, -3, // 110-119
8, -3, -3, -1, 6, -3, -3, -3, -3, -3, // 120-129
-3, 10, 6, 2, 14, 8, -3, 4, -3, -3, // 130-139
-3, -3, -3, -3, -3, -3, -3, -3, 2, -3, // 140-149
-3, -3, -3, 8, 8, 6, 8, 3, -3, -3, // 150-159
-3, 8, 8, -3, -3, -3, 6, -1, 6, -3, // 160-169
6, -3, -3, -3, -3, 2, -3, 2, -1, 4, // 170-179
2, -3, -3, -3, 0, -3, -3, -3, 9, -3, // 180-189
-3, -3, -3, -3, 6, 8, 6, -3, -3, 6, // 190-199
-3, -1, -3, -3, -3, -3, 8, -3, -3, -3, // 200-209
-3, -3, -3, 8, -3, -1, -3, -3, 2, -3, // 210-219
-3, -3, -3, -3, -3, -3, -3, -3, 6, -3, // 220-229
-3, 9, -3, 12, 6, -3, -3, -1, -3, 8, // 230-239
-3, -3, -3, 6, 8, 0, -3, 6, 10, -3, // 240-249
-3, -3, -3, 14, 6, -3 // 250-255
};
/**
* Constructs a new {@code GameReadEvent}.
* @param session The session.
* @param buffer The buffer to read from.
*/
public GameReadEvent(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 (session == null || session.getPlayer() == null) {
continue;
}
if (opcode >= PACKET_SIZES.length) {
break;
}
int header = PACKET_SIZES[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);
IoBuffer buf = new IoBuffer(opcode, null, ByteBuffer.wrap(data));
//SystemLogger.log("Packet opcode " + opcode + "received.");
//IncomingPacket packet = PacketRepository.getIncoming(opcode);
session.setLastPing(System.currentTimeMillis());
last = opcode;
Packet processed = Decoders530.process(session.getPlayer(), opcode, buf);
if (processed instanceof Packet.UnhandledOp) {
SystemLogger.logWarn(this.getClass(), "Unhandled opcode: " + opcode);
} else if (processed instanceof Packet.DecodingError) {
SystemLogger.logErr(this.getClass(), ((Packet.DecodingError) processed).getMessage());
} else if (!(processed instanceof Packet.NoProcess)) {
PacketProcessor.enqueue(processed);
}
}
}
/**
* 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;
}
if(header == -3){
System.out.println(buffer.remaining());
}
SystemLogger.logErr(this.getClass(), "Invalid packet [opcode=" + opcode + ", last=" + last + ", queued=" + usedQueuedBuffer + "], header=" + header+"!");
return -1;
}
}
@@ -0,0 +1,54 @@
package core.net.event;
import core.net.IoSession;
import core.net.IoWriteEvent;
import core.net.packet.IoBuffer;
import java.nio.ByteBuffer;
/**
* Handles game packet writing events.
* @author Emperor
*/
public final class GameWriteEvent extends IoWriteEvent {
/**
* Constructs a new {@code GameWriteEvent}.
* @param session The session.
* @param context The context.
*/
public GameWriteEvent(IoSession session, Object context) {
super(session, context);
}
@Override
public void write(IoSession session, Object context) {
if (context instanceof ByteBuffer) {
session.queue((ByteBuffer) context);
return;
}
IoBuffer buffer = (IoBuffer) context;
ByteBuffer buf = (ByteBuffer) buffer.toByteBuffer().flip();
if (buf == null) {
throw new RuntimeException("Critical networking error: The byte buffer requested was null.");
}
if (buffer.opcode() != -1) {
int packetLength = buf.remaining() + 4;
ByteBuffer response = ByteBuffer.allocate(packetLength);
response.put((byte) buffer.opcode());
switch (buffer.getHeader()) {
case BYTE:
response.put((byte) buf.remaining());
break;
case SHORT:
response.putShort((short) buf.remaining());
break;
default:
break;
}
buf = (ByteBuffer) response.put(buf).flip();
}
session.queue(buf);
}
}
@@ -0,0 +1,73 @@
package core.net.event;
import core.net.IoReadEvent;
import core.net.IoSession;
import core.net.lobby.WorldList;
import core.net.registry.AccountRegister;
import core.tools.RandomFunction;
import core.tools.SystemLogger;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
/**
* Handles handshake read events.
* @author Emperor
*/
public final class HSReadEvent extends IoReadEvent {
// debug
static Map<String, Integer> count = new HashMap<>();
/**
* 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) {
Integer amount = count.get(session.getAddress());
if (amount == null) {
amount = 0;
}
count.put(session.getAddress(), amount + 1);
int opcode = buffer.get() & 0xFF;
switch (opcode) {
case 14:
session.setNameHash(buffer.get() & 0xFF);
session.setServerKey(RandomFunction.RANDOM.nextLong());
session.write(true);
break;
case 15:
int revision = buffer.getInt();
//int sub_revision = buffer.getInt();
buffer.flip();
System.out.println(buffer.limit());
if (revision != 530 ){//|| sub_revision != Constants.CLIENT_BUILD) {
session.disconnect();
break;
}
session.write(false);
break;
case 147:
case 186:
case 36:
AccountRegister.read(session, opcode, buffer);
break;
case 255: // World list
int updateStamp = buffer.getInt();
WorldList.sendUpdate(session, updateStamp);
break;
default:
SystemLogger.logInfo(this.getClass(), "PKT " + opcode);
session.disconnect();
break;
}
}
}
@@ -0,0 +1,49 @@
package core.net.event;
import core.net.IoSession;
import core.net.IoWriteEvent;
import core.net.producer.JS5EventProducer;
import core.net.producer.LoginEventProducer;
import java.nio.ByteBuffer;
/**
* Handles Handshake write events.
* @author Emperor
*/
public final class HSWriteEvent extends IoWriteEvent {
/**
* The JS-5 event producer.
*/
private static final JS5EventProducer JS5_PRODUCER = new JS5EventProducer();
/**
* The login event producer.
*/
private static final LoginEventProducer LOGIN_PRODUCER = new LoginEventProducer();
/**
* 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) 0);
if ((Boolean) context) {
buffer.putLong(session.getServerKey());
session.setProducer(LOGIN_PRODUCER);
} else {
session.setProducer(JS5_PRODUCER);
}
buffer.flip();
session.queue(buffer);
}
}
@@ -0,0 +1,81 @@
package core.net.event;
import core.cache.misc.buffer.ByteBufferUtils;
import core.net.IoReadEvent;
import core.net.IoSession;
import java.nio.ByteBuffer;
/**
* Handles JS-5 reading events.
* @author Emperor
*/
public final class JS5ReadEvent extends IoReadEvent {
/**
* Constructs a new {@code JS5ReadEvent}.
* @param session The session.
* @param buffer The buffer.
*/
public JS5ReadEvent(IoSession session, ByteBuffer buffer) {
super(session, buffer);
}
@Override
public void read(IoSession session, ByteBuffer buffer) {
while (buffer.hasRemaining()) {
int opcode = buffer.get() & 0xFF;
//System.out.println("JS5 initiated! Opcode: " + opcode);
if (buffer.remaining() < 3) {
queueBuffer(opcode);
return;
}
switch (opcode) {
case 0:
case 1:
int request = ByteBufferUtils.getMedium(buffer);
//System.out.println("Requested file " + request);
int container = request >> 16 & 0xFF;
//System.out.println("In container " + container);
int archive = request & 0xFFFF;
//System.out.println("In archive " + archive);
session.getJs5Queue().queue(container, archive, opcode == 1);
break;
case 2: // music
case 3: // Music
buffer.get();
buffer.getShort();
break;
case 4:
session.setJs5Encryption(buffer.get());
if (buffer.getShort() != 0) {
session.disconnect();
return;
}
break;
case 5:
case 9:
if (buffer.remaining() < 4) {
queueBuffer(opcode);
return;
}
buffer.getInt();
break;
case 6:
ByteBufferUtils.getMedium(buffer); // Value should be 3
// buffer.getShort(); // Value should be 0
break;
case 7:
buffer.get();
buffer.getShort();
session.disconnect();
return;
default:
System.out.println("Unhandled JS5 opcode " + opcode + "!");
buffer.get();
buffer.getShort();
break;
}
}
}
}
@@ -0,0 +1,71 @@
package core.net.event;
import core.cache.Cache;
import core.net.IoSession;
import core.net.IoWriteEvent;
import java.nio.ByteBuffer;
/**
* Handles JS-5 writing events.
* @author Emperor
*/
public final class JS5WriteEvent extends IoWriteEvent {
/**
* The cached reference data.
*/
private static byte[] cachedReference;
/**
* Constructs a new {@code JS5WriteEvent}.
* @param session The session.
* @param context The event context.
*/
public JS5WriteEvent(IoSession session, Object context) {
super(session, context);
}
@Override
public void write(IoSession session, Object context) {
int[] request = (int[]) context;
int container = request[0];
int archive = request[1];
boolean priority = request[2] == 1;
if (archive == 255 && container == 255) {
session.queue(getReferenceData());
return;
}
ByteBuffer response = Cache.getArchiveData(container, archive, priority, session.getJs5Encryption());
if (response != null) {
session.queue(response);
}
}
/**
* Gets the reference data.
* @return The reference data.
*/
private static ByteBuffer getReferenceData() {
if (cachedReference == null) {
cachedReference = Cache.generateReferenceData();
}
ByteBuffer buffer = ByteBuffer.allocate(cachedReference.length << 2);
buffer.put((byte) 255);
buffer.putShort((short) 255);
buffer.put((byte) 0);
buffer.putInt(cachedReference.length);
int offset = 10;
for (int index = 0; index < cachedReference.length; index++) {
if (offset == 512) {
buffer.put((byte) 255);
offset = 1;
}
buffer.put(cachedReference[index]);
offset++;
}
buffer.flip();
return buffer;
}
}
@@ -0,0 +1,37 @@
package core.net.event
import core.game.node.entity.player.info.ClientInfo
import core.game.node.entity.player.info.PlayerDetails
import core.net.IoReadEvent
import core.net.IoSession
import core.auth.AuthResponse
import core.game.world.GameWorld
import core.net.packet.`in`.Login
import java.nio.ByteBuffer
/**
* Handles login reading events.
* @author Ceikry
*/
class LoginReadEvent(session: IoSession?, buffer: ByteBuffer?) : IoReadEvent(session, buffer) {
override fun read(session: IoSession, buffer: ByteBuffer) {
val (response, info) = Login.decodeFromBuffer(buffer)
if(response != AuthResponse.Success || info == null) {
session.write(response)
return
}
val (authResponse, accountInfo) = GameWorld.authenticator.checkLogin(info.username, info.password)
if(authResponse != AuthResponse.Success || accountInfo == null) {
session.write(authResponse)
return
}
val details = PlayerDetails(info.username)
details.accountInfo = accountInfo
details.communication.parse(accountInfo)
session.clientInfo = ClientInfo(info.displayMode, info.windowMode, info.screenWidth, info.screenHeight)
session.isaacPair = info.isaacPair
session.associatedUsername = info.username
Login.proceedWith(session, details, info.opcode)
}
}
@@ -0,0 +1,75 @@
package core.net.event;
import core.game.node.entity.player.Player;
import core.game.node.entity.player.info.login.Response;
import core.net.EventProducer;
import core.net.IoSession;
import core.net.IoWriteEvent;
import core.net.producer.GameEventProducer;
import core.auth.AuthResponse;
import java.nio.ByteBuffer;
/**
* Handles login writing events.
* @author Emperor
*/
public final class LoginWriteEvent extends IoWriteEvent {
/**
* The game event producer.
*/
private static final EventProducer GAME_PRODUCER = new GameEventProducer();
/**
* Constructs a new {@code LoginWriteEvent}.
* @param session The session.
* @param context The event context.
*/
public LoginWriteEvent(IoSession session, Object context) {
super(session, context);
}
@Override
public void write(IoSession session, Object context) {
AuthResponse response = (AuthResponse) context;
ByteBuffer buffer = ByteBuffer.allocate(500);
buffer.put((byte) response.ordinal());
switch (response.ordinal()) {
case 2: //successful login
buffer.put(getWorldResponse(session));
session.setProducer(GAME_PRODUCER);
break;
//Could add a case here to auto-restart the server in case the login server goes offline (case 8)
//Possibly a risk for malicious attacks though
case 21: //Moving world
buffer.put((byte) session.getServerKey());
break;
}
buffer.flip();
session.queue(buffer);
}
/**
* Gets the world response buffer.
* @param session The session.
* @return The buffer.
*/
private static ByteBuffer getWorldResponse(IoSession session) {
ByteBuffer buffer = ByteBuffer.allocate(150);
Player player = session.getPlayer();
buffer.put((byte) player.getDetails().getRights().ordinal());
buffer.put((byte) 0);
buffer.put((byte) 0);
buffer.put((byte) 0);
buffer.put((byte) 1);
buffer.put((byte) 0);
buffer.put((byte) 0);
buffer.putShort((short) player.getIndex());
buffer.put((byte) (1)); // Enable all G.E boxes
buffer.put((byte) 1);
buffer.flip();
return buffer;
}
}
@@ -0,0 +1,39 @@
package core.net.event;
import core.net.EventProducer;
import core.net.IoReadEvent;
import core.net.IoSession;
import core.net.producer.RegistryEventProducer;
import java.nio.ByteBuffer;
/**
* Handles the management server handshake read events.
* @author Emperor
*/
public final class MSHSReadEvent extends IoReadEvent {
/**
* The event producer.
*/
private static final EventProducer REGISTRY_PRODUCER = new RegistryEventProducer();
/**
* Constructs a new {@code MSHSReadEvent} {@Code Object}
* @param session The session.
* @param buffer The buffer to read.
*/
public MSHSReadEvent(IoSession session, ByteBuffer buffer) {
super(session, buffer);
}
@Override
public void read(IoSession session, ByteBuffer buffer) {
int opcode = buffer.get() & 0xFF;
if (opcode == 14) {
session.setProducer(REGISTRY_PRODUCER);
session.write(true);
}
}
}
@@ -0,0 +1,33 @@
package core.net.event;
import core.cache.misc.buffer.ByteBufferUtils;
import core.net.IoSession;
import core.net.IoWriteEvent;
import core.ServerConstants;
import java.nio.ByteBuffer;
/**
* Handles the management server handshake write event.
* @author Emperor
*/
public final class MSHSWriteEvent extends IoWriteEvent {
/**
* Constructs a new {@code MSHSWriteEvent} {@code Object}
* @param session The session.
* @param context The context.
*/
public MSHSWriteEvent(IoSession session, Object context) {
super(session, context);
}
@Override
public void write(IoSession session, Object context) {
ByteBuffer buffer = ByteBuffer.allocate(2 + ServerConstants.MS_SECRET_KEY.length());
buffer.put((byte) 88);
ByteBufferUtils.putString(ServerConstants.MS_SECRET_KEY, buffer);
session.queue((ByteBuffer) buffer.flip());
}
}
@@ -0,0 +1,92 @@
package core.net.event;
import core.tools.SystemLogger;
import core.net.IoReadEvent;
import core.net.IoSession;
import java.nio.ByteBuffer;
/**
* Handles reading Management server packets.
* @author Emperor
*/
public final class MSReadEvent extends IoReadEvent {
/**
* The packet sizes.
*/
private static final int[] PACKET_SIZE = { -1, -1, -1, -2, -1, -1, -2, -1, -2, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
/**
* Constructs a new {@code MSReadEvent} {@code Object}
* @param session The I/O session.
* @param buffer The buffer to read from.
*/
public MSReadEvent(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;
}
}
/**
* 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;
}
SystemLogger.logErr(this.getClass(), "Invalid packet [opcode=" + opcode + ", last=" + last + ", queued=" + usedQueuedBuffer + "]!");
return -1;
}
}
@@ -0,0 +1,44 @@
package core.net.event;
import core.net.IoSession;
import core.net.IoWriteEvent;
import core.net.packet.IoBuffer;
import java.nio.ByteBuffer;
/**
* Handles management server write events.
* @author Emperor
*/
public final class MSWriteEvent extends IoWriteEvent {
/**
* Constructs a new {@code MSWriteEvent} {@code Object}
* @param session The I/O session.
* @param context The packet context.
*/
public MSWriteEvent(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,60 @@
package core.net.event;
import core.tools.SystemLogger;
import core.game.world.GameWorld;
import core.net.IoReadEvent;
import core.net.IoSession;
import core.net.amsc.ManagementServerState;
import core.net.amsc.WorldCommunicator;
import core.net.producer.MSEventProducer;
import java.nio.ByteBuffer;
/**
* Handles world registry read events.
* @author Emperor
*/
public final class RegistryReadEvent extends IoReadEvent {
/**
* The event producer.
*/
private static final MSEventProducer PRODUCER = new MSEventProducer();
/**
* 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 opcode = buffer.get() & 0xFF;
switch (opcode) {
case 0:
WorldCommunicator.setState(ManagementServerState.NOT_AVAILABLE);
SystemLogger.logErr(this.getClass(), "Failed registering world to AMS - [id=" + GameWorld.getSettings().getWorldId() + ", cause=World id out of bounds]!");
break;
case 1:
session.setProducer(PRODUCER);
WorldCommunicator.setState(ManagementServerState.AVAILABLE);
SystemLogger.logInfo(this.getClass(), "Successfully registered world to AMS - [id=" + GameWorld.getSettings().getWorldId() + "]!");
break;
case 2:
WorldCommunicator.setState(ManagementServerState.NOT_AVAILABLE);
SystemLogger.logErr(this.getClass(), "Failed registering world to AMS - [id=" + GameWorld.getSettings().getWorldId() + ", cause=World id already in use]!");
break;
case 3:
WorldCommunicator.setState(ManagementServerState.NOT_AVAILABLE);
SystemLogger.logErr(this.getClass(), "Failed registering world to AMS - [id=" + GameWorld.getSettings().getWorldId() + ", cause=Exception in AMS]!");
break;
default:
System.out.println("??" + opcode);
break;
}
}
}
@@ -0,0 +1,46 @@
package core.net.event;
import core.cache.misc.buffer.ByteBufferUtils;
import core.game.world.GameWorld;
import core.net.Constants;
import core.net.IoSession;
import core.net.IoWriteEvent;
import java.nio.ByteBuffer;
/**
* Handles game world registry writing events.
* @author Emperor
*/
public final class RegistryWriteEvent extends IoWriteEvent {
/**
* The string check.
*/
private static final String CHECK = "12x4578f5g45hrdjiofed59898";
/**
* 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(128);
buffer.put((byte) GameWorld.getSettings().getWorldId());
buffer.putInt(Constants.REVISION);
buffer.put((byte) GameWorld.getSettings().getCountryIndex());
buffer.put((byte) (GameWorld.getSettings().isMembers() ? 1 : 0));
buffer.put((byte) (GameWorld.getSettings().isPvp() ? 1 : 0));
buffer.put((byte) (GameWorld.getSettings().isQuickChat() ? 1 : 0));
buffer.put((byte) (GameWorld.getSettings().isLootshare() ? 1 : 0));
ByteBufferUtils.putString(GameWorld.getSettings().getActivity(), buffer);
buffer.put(CHECK.getBytes());
session.queue((ByteBuffer) buffer.flip());
}
}
@@ -0,0 +1,149 @@
package core.net.lobby;
import core.game.world.GameWorld;
import core.game.world.repository.Repository;
/**
* Represents a world's definition.
* @author Dementhium development team.
*
*/
public class WorldDefinition {
/**
* The activity for this world.
*/
private final String activity;
/**
* The coutry flag.
*/
private final int country;
/**
* If the world is members.
*/
private final int flag;
/**
* The ip-address for this world.
*/
private final String ip;
/**
* The location.
*/
private final int location;
/**
* The region.
*/
private final String region;
/**
* The world's id.
*/
private final int worldId;
/**
* The amount of players in this world.
*/
private int players;
/**
* Constructs a new {@code WorldDefinition} {@code Object}.
* @param worldId The world's id.
* @param location The location.
* @param flag If the world is members.
* @param activity The activity for this world.
* @param ip The IP-address.
* @param region The region.
* @param country The country flag.
*/
public WorldDefinition(int worldId, int location, int flag, String activity, String ip, String region, int country) {
this.worldId = worldId;
this.location = location;
this.flag = flag;
this.activity = activity;
this.ip = ip;
this.region = region;
this.country = country;
}
/**
* @return the activity
*/
public String getActivity() {
return activity;
}
/**
* @return the country
*/
public int getCountry() {
return country;
}
/**
* @return the flag
*/
public int getFlag() {
return flag;
}
/**
* @return the ip
*/
public String getIp() {
return ip;
}
/**
* @return the location
*/
public int getLocation() {
return location;
}
/**
* @return the region
*/
public String getRegion() {
return region;
}
/**
* @return the worldId
*/
public int getWorldId() {
return worldId;
}
/**
* Gets the player count.
* @return The player count.
*/
public int getPlayerCount() {
if (worldId == GameWorld.getSettings().getWorldId()) {
return Repository.getPlayers().size();
}
return players;
}
/**
* Gets the players.
* @return the players
*/
public int getPlayers() {
return players;
}
/**
* Sets the baplayers.
* @param players the players to set.
*/
public void setPlayers(int players) {
this.players = players;
}
}
@@ -0,0 +1,214 @@
package core.net.lobby;
import core.game.world.GameWorld;
import core.net.IoSession;
import core.net.packet.IoBuffer;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* Handles the world list.
* @author Emperor
*
*/
public final class WorldList {
/**
* The value for Australia.
*/
public static final int COUNTRY_AUSTRALIA = 16;
/**
* The value for Belgium.
*/
public static final int COUNTRY_BELGIUM = 22;
/**
* The value for Brazil.
*/
public static final int COUNTRY_BRAZIL = 31;
/**
* The value for Canada.
*/
public static final int COUNTRY_CANADA = 38;
/**
* The value for Denmark.
*/
public static final int COUNTRY_DENMARK = 58;
/**
* The value for Finland.
*/
public static final int COUNTRY_FINLAND = 69;
/**
* The value for Ireland.
*/
public static final int COUNTRY_IRELAND = 101;
/**
* The value for Mexico.
*/
public static final int COUNTRY_MEXICO = 152;
/**
* The value for the Netherlands.
*/
public static final int COUNTRY_NETHERLANDS = 161;
/**
* The value for Norway.
*/
public static final int COUNTRY_NORWAY = 162;
/**
* The value for Sweden.
*/
public static final int COUNTRY_SWEDEN = 191;
/**
* The value for the UK.
*/
public static final int COUNTRY_UK = 77;
/**
* The value for USA.
*/
public static final int COUNTRY_USA = 225;
/**
* If the world is free to play.
*/
public static final int FLAG_NON_MEMBERS = 0;
/**
* If the world is a members world.
*/
public static final int FLAG_MEMBERS = 1;
/**
* If the world is a quick chat world
*/
public static final int FLAG_QUICK_CHAT = 2;
/**
* If the world is a PvP-world.
*/
public static final int FLAG_PVP = 4;
/**
* If the world is a lootshare world.
*/
public static final int FLAG_LOOTSHARE = 8;
/**
* A list holding all the currently loaded worlds.
*/
private static final List<WorldDefinition> WORLD_LIST = new ArrayList<WorldDefinition>();
/**
* The last update time stamp (in server ticks).
*/
private static int updateStamp = 0;
/**
* Populates the world list.
*/
static {
addWorld(new WorldDefinition(1, 0, FLAG_MEMBERS | FLAG_LOOTSHARE, "2009Scape Classic", "127.0.0.1", "Anywhere, USA", COUNTRY_USA));
}
/**
* Adds a world to the world list.
* @param def The world definitions.
*/
public static void addWorld(WorldDefinition def) {
WORLD_LIST.add(def);
flagUpdate();
}
/**
* Gets the packet to update the world list in the lobby.
* @return The {@code OutgoingPacket} to write.
*/
public static void sendUpdate(IoSession session, int updateStamp) {
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.put((byte) 0);
buf.putShort((short) 0);
buf.put((byte) 1);
IoBuffer buffer = new IoBuffer();
if (updateStamp != WorldList.updateStamp) {
buf.put((byte) 1); // Indicates an update occured.
putWorldListinfo(buffer);
} else {
buf.put((byte) 0);
}
putPlayerInfo(buffer);
if (buffer.toByteBuffer().position() > 0) {
buf.put((ByteBuffer) buffer.toByteBuffer().flip());
}
buf.putShort(1, (short) (buf.position() - 3));
session.queue((ByteBuffer) buf.flip());
}
/**
* Adds the world configuration on the packet.
* @param buffer The current packet.
*/
private static void putWorldListinfo(IoBuffer buffer) {
buffer.putSmart(WORLD_LIST.size());
putCountryInfo(buffer);
buffer.putSmart(0);
buffer.putSmart(WORLD_LIST.size());
buffer.putSmart(WORLD_LIST.size());
for (WorldDefinition w : WORLD_LIST) {
buffer.putSmart(w.getWorldId());
buffer.put(w.getLocation());
buffer.putInt(w.getFlag());
buffer.putJagString(w.getActivity());
buffer.putJagString(w.getIp());
}
buffer.putInt(updateStamp);
}
/**
* Adds the world status on the packet.
* @param buffer The current packet.
*/
private static void putPlayerInfo(IoBuffer buffer) {
for (WorldDefinition w : WORLD_LIST) {
buffer.putSmart(w.getWorldId());
buffer.putShort(w.getPlayerCount());
}
}
/**
* Sets the countries for each world.
* @param buffer The current packet.
*/
private static void putCountryInfo(IoBuffer buffer) {
for (WorldDefinition w : WORLD_LIST) {
buffer.putSmart(w.getCountry());
buffer.putJagString(w.getRegion());
}
}
/**
* Gets the updateStamp.
* @return the updateStamp
*/
public static int getUpdateStamp() {
return updateStamp;
}
/**
* Sets the baupdateStamp.
*/
public static void flagUpdate() {
WorldList.updateStamp = GameWorld.getTicks();
}
}
@@ -0,0 +1,17 @@
package core.net.packet;
import core.game.node.entity.player.Player;
/**
* Represents packet context.
* @author Emperor
*/
public interface Context {
/**
* Gets the node.
* @return The node.
*/
public Player getPlayer();
}
@@ -0,0 +1,20 @@
package core.net.packet;
import core.game.node.entity.player.Player;
/**
* Represents an incoming packet.
* @author Emperor
*/
public interface IncomingPacket {
/**
* Decodes the incoming packet.
* @param player The player.
* @param opcode The opcode.
* @param buffer The buffer.
* @return The new buffer to send in response.
*/
public void decode(Player player, int opcode, IoBuffer buffer);
}
@@ -0,0 +1,638 @@
package core.net.packet;
import core.cache.crypto.ISAACCipher;
import core.cache.misc.buffer.ByteBufferUtils;
import java.nio.ByteBuffer;
/**
* 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 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]);
}
}
/**
* 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 putReverse(byte[] data, int start, int offset) {
for (int i = offset + start; i >= start; i--) {
put(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() & 0xFF);
}
/**
* @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 <= (0xFF & peek)) {
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;
}
public void cypherOpcode(ISAACCipher cipher){
this.opcode += (byte)cipher.getNextValue();
}
/**
* @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,16 @@
package core.net.packet;
/**
* Represents an outgoing packet.
* @author Emperor
* @param <T> The context type.
*/
public interface OutgoingPacket<Context> {
/**
* Sends the packet.
* @param context The context.
*/
public void send(Context context);
}
@@ -0,0 +1,24 @@
package core.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,759 @@
package core.net.packet
import core.game.event.ButtonClickEvent
import core.api.getAttribute
import core.api.sendMessage
import core.api.tryPop
import core.cache.def.impl.ItemDefinition
import core.cache.def.impl.NPCDefinition
import core.cache.def.impl.SceneryDefinition
import core.game.container.Container
import core.game.container.impl.BankContainer
import core.game.interaction.PluginInteractionManager
import core.game.interaction.Interaction
import core.game.interaction.MovementPulse
import core.game.interaction.NodeUsageEvent
import core.game.interaction.Option
import core.game.interaction.UseWithHandler
import core.game.node.Node
import core.game.node.entity.player.Player
import core.game.node.entity.player.info.Rights
import core.game.node.entity.player.info.login.LoginConfiguration
import core.game.node.entity.player.link.SpellBookManager
import core.game.node.entity.combat.spell.MagicSpell
import content.global.skill.summoning.familiar.FamiliarSpecial
import core.game.node.item.GroundItemManager
import core.game.node.item.Item
import core.game.node.scenery.Scenery
import core.game.system.communication.ClanRank
import core.game.system.communication.CommunicationInfo
import core.game.system.task.Pulse
import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.game.world.update.flag.context.ChatMessage
import core.game.world.update.flag.player.ChatFlag
import core.net.amsc.MSPacketRepository
import core.net.packet.context.PlayerContext
import core.net.packet.out.ClearMinimapFlag
import org.rs09.consts.Components
import proto.management.ClanMessage
import proto.management.JoinClanRequest
import proto.management.LeaveClanRequest
import core.ServerConstants
import core.game.ge.GrandExchange.Companion.getOfferStats
import core.game.ge.GrandExchange.Companion.getRecommendedPrice
import core.game.ge.GrandExchangeOffer
import core.game.ge.PriceIndex
import core.game.interaction.IntType
import core.game.interaction.InteractionListeners
import core.game.interaction.InterfaceListeners
import content.global.handlers.iface.ge.StockMarket
import content.global.skill.magic.SpellListener
import content.global.skill.magic.SpellListeners
import content.global.skill.magic.SpellUtils
import core.game.node.entity.player.info.LogType
import core.game.node.entity.player.info.PlayerMonitor
import core.tools.SystemLogger
import core.game.system.command.CommandSystem
import core.game.world.GameWorld
import core.game.world.repository.Repository
import core.net.packet.`in`.Packet
import core.net.packet.`in`.RunScript
import core.worker.ManagementEvents
import java.io.PrintWriter
import java.io.StringWriter
import java.util.*
object PacketProcessor {
val queue = LinkedList<Packet>()
@JvmStatic fun enqueue(pkt: Packet) {
queue.addLast(pkt)
}
@JvmStatic fun processQueue() {
var countThisCycle = queue.size
val sw = StringWriter()
val pw = PrintWriter(sw)
var pkt: Packet
while (countThisCycle-- > 0) {
pkt = queue.tryPop(Packet.NoProcess())
try {
process(pkt)
} catch (e: Exception) {
e.printStackTrace(pw)
SystemLogger.logErr(this::class.java, "Error Processing ${pkt::class.java.simpleName}: $sw")
}
}
}
private fun process(pkt: Packet) {
when (pkt) {
is Packet.ItemAction -> processItemAction(pkt)
is Packet.NpcAction -> processNpcAction(pkt)
is Packet.SceneryAction -> processSceneryAction(pkt)
is Packet.PlayerAction -> processPlayerAction(pkt)
is Packet.GroundItemAction -> processGroundItemAction(pkt)
is Packet.UseWithNpc,
is Packet.UseWithItem,
is Packet.UseWithScenery,
is Packet.UseWithGroundItem,
is Packet.UseWithPlayer -> processUseWith(pkt)
is Packet.IfAction -> processIfAction(pkt)
is Packet.CloseIface -> processCloseIface(pkt)
is Packet.WorldspaceWalk,
is Packet.MinimapWalk,
is Packet.InteractWalk -> processWalkPacket(pkt)
is Packet.AddFriend -> CommunicationInfo.add(pkt.player, pkt.username)
is Packet.RemoveFriend -> CommunicationInfo.remove(pkt.player, pkt.username, false)
is Packet.AddIgnore -> CommunicationInfo.block(pkt.player, pkt.username)
is Packet.RemoveIgnore -> CommunicationInfo.remove(pkt.player, pkt.username, true)
is Packet.ComponentItemAction,
is Packet.ComponentPlayerAction,
is Packet.ComponentNpcAction,
is Packet.ComponentSceneryAction,
is Packet.ComponentGroundItemAction -> processComponentUseWith(pkt)
is Packet.SlotSwitchSingleComponent,
is Packet.SlotSwitchMultiComponent -> processSlotSwitch(pkt)
is Packet.TrackingMouseClick,
is Packet.TrackingAfkTimeout,
is Packet.TrackingFocus,
is Packet.TrackingCameraPos,
is Packet.TrackingDisplayUpdate -> processTrackingPacket(pkt)
is Packet.PlayerPrefsUpdate -> {/*TODO implement something that cares about this */}
is Packet.Ping -> pkt.player.session.lastPing = System.currentTimeMillis()
is Packet.JoinClan -> {
if (pkt.clanName.isEmpty() && pkt.player.communication.currentClan.isNotEmpty()) {
val builder = LeaveClanRequest.newBuilder()
builder.clanName = pkt.player.communication.currentClan
builder.username = pkt.player.name
ManagementEvents.publish(builder.build())
return
}
sendMessage(pkt.player, "Attempting to join channel....:clan:")
val builder = JoinClanRequest.newBuilder()
builder.clanName = pkt.clanName
builder.username = pkt.player.name
ManagementEvents.publish(builder.build())
}
is Packet.SetClanRank -> {
CommunicationInfo.updateClanRank(pkt.player, pkt.username, ClanRank.values()[pkt.rank + 1])
}
is Packet.KickFromClan -> {
val clan = pkt.player.communication.clan ?: return
val target = Repository.getPlayerByName(pkt.username) ?: return
clan.kick(pkt.player, target)
}
is Packet.QuickChat -> QCRepository.sendQC(pkt.player, pkt.multiplier, pkt.offset, pkt.type, pkt.indexA, pkt.indexB, pkt.forClan)
is Packet.InputPromptResponse -> {
val script: ((Any) -> Boolean) = pkt.player.getAttribute<((Any) -> Boolean)?>("runscript", null) ?: return
if (pkt.player.locks.isInteractionLocked)
return
try {
RunScript.processInput(pkt.player, pkt.response, script)
} finally {
pkt.player.removeAttribute("parseamount")
pkt.player.removeAttribute("runscript")
}
}
is Packet.ReportAbuse -> {
if (!GameWorld.accountStorage.checkUsernameTaken(pkt.target.lowercase())) {
sendMessage(pkt.player, "Invalid player name.")
return
}
if (pkt.target.equals(pkt.player.username, true)){
pkt.player.sendMessage("You can't report yourself!")
}
core.game.global.report.AbuseReport(
pkt.player.name,
pkt.target,
core.game.global.report.Rule.forId(pkt.rule)
).construct(pkt.player, pkt.modMute)
}
is Packet.TrackFinished -> {
if (pkt.player.musicPlayer.isLooping)
pkt.player.musicPlayer.replay()
else
pkt.player.musicPlayer.isPlaying = false
}
is Packet.GESetOfferItem -> {
val offer = pkt.player.getAttribute("ge-temp", GrandExchangeOffer())
val index = pkt.player.getAttribute("ge-index", -1)
offer.itemID = pkt.itemId
offer.sell = false
if (!PriceIndex.canTrade(pkt.itemId)) {
sendMessage(pkt.player, "That item is blacklisted from the grand exchange.")
return
}
offer.player = pkt.player
offer.amount = 1
offer.offeredValue = getRecommendedPrice(pkt.itemId, false)
offer.index = index
StockMarket.updateVarbits(pkt.player, offer, index, false)
pkt.player.setAttribute("ge-temp", offer)
pkt.player.packetDispatch.sendString(getOfferStats(pkt.itemId, false), 105, 142)
pkt.player.interfaceManager.closeChatbox()
}
is Packet.Command -> {
PlayerMonitor.log(pkt.player, LogType.COMMAND, pkt.commandLine)
CommandSystem.commandSystem.parse(pkt.player, pkt.commandLine)
}
is Packet.ChatMessage -> {
if (pkt.player.details.isMuted)
pkt.player.sendMessage("You have been muted due to breaking a rule.")
else {
if (pkt.message.startsWith("/") && pkt.player.communication.clan != null) {
val builder = ClanMessage.newBuilder()
builder.sender = pkt.player.name
builder.clanName = pkt.player.communication.clan.owner.lowercase().replace(" ", "_")
builder.message = pkt.message.substring(1)
builder.rank = pkt.player.rights.ordinal
ManagementEvents.publish(builder.build())
return
}
PlayerMonitor.logChat(pkt.player, "public", pkt.message)
val ctx = ChatMessage(pkt.player, pkt.message, pkt.effects, pkt.message.length)
pkt.player.updateMasks.register(ChatFlag(ctx))
}
}
is Packet.ChatSetting -> {
MSPacketRepository.sendChatSetting(pkt.player, pkt.public, pkt.private, pkt.trade)
pkt.player.settings.updateChatSettings(pkt.public, pkt.private, pkt.trade)
}
is Packet.PrivateMessage -> {
if (pkt.player.details.isMuted)
pkt.player.sendMessage("You have been muted due to breaking a rule.")
else
CommunicationInfo.sendMessage(pkt.player, pkt.username, pkt.message)
PlayerMonitor.logPrivateChat(pkt.player, pkt.username, pkt.message)
}
is Packet.PacketCountUpdate -> {
val final = pkt.count - pkt.player.interfaceManager.getPacketCount(0)
pkt.player.interfaceManager.getPacketCount(final)
}
is Packet.ContinueOption -> {
val player = pkt.player
player.debug("[CONTINUE OPT]----------")
player.debug("Iface: ${pkt.iface}")
player.debug("Child: ${pkt.child}")
player.debug("Slot: ${pkt.slot}")
player.debug("------------------------")
if (player.dialogueInterpreter.dialogue == null) {
player.interfaceManager.closeChatbox()
player.dialogueInterpreter.actions.removeFirstOrNull()?.handle(player, pkt.child)
val component = player.interfaceManager.getComponent(pkt.iface) ?: return
if (!InterfaceListeners.run(player, component, pkt.opcode, pkt.child, pkt.slot, -1))
component.plugin?.handle(player, component, pkt.opcode, pkt.child, pkt.slot, -1)
return
}
player.dialogueInterpreter.handle(pkt.iface, pkt.child)
}
is Packet.ItemExamine -> {
val def = ItemDefinition.forId(pkt.id) ?: return
pkt.player.debug("[ITEM] ID: ${pkt.id} Value: ${def.value}")
pkt.player.sendMessage(def.examine)
}
is Packet.SceneryExamine -> {
val def = SceneryDefinition.forId(pkt.id) ?: return
pkt.player.debug("[SCENERY]---------------------")
pkt.player.debug("ID: ${pkt.id}")
if (def.configFile != null)
pkt.player.debug("Varbit: ${def.configFile.id}")
pkt.player.debug("------------------------------")
pkt.player.sendMessage(def.examine)
}
is Packet.NpcExamine -> {
val def = NPCDefinition.forId(pkt.id)
pkt.player.debug("[NPC]-------------------------")
pkt.player.debug("ID: ${pkt.id}")
if (def.configFileId != -1)
pkt.player.debug("Varbit: ${def.configFileId}")
pkt.player.debug("------------------------------")
pkt.player.sendMessage(def.examine)
}
else -> SystemLogger.logWarn(this::class.java, "Unprocessed Packet: ${pkt::class.java.simpleName}")
}
}
private fun processTrackingPacket(pkt: Packet) {
when(pkt) {
is Packet.TrackingFocus -> {}
is Packet.TrackingDisplayUpdate -> {
pkt.player.session.clientInfo.screenWidth = pkt.screenWidth
pkt.player.session.clientInfo.screenHeight = pkt.screenHeight
pkt.player.session.clientInfo.displayMode = pkt.displayMode
pkt.player.interfaceManager.switchWindowMode(pkt.windowMode)
}
is Packet.TrackingAfkTimeout -> {
if (pkt.player.details.rights != Rights.ADMINISTRATOR)
pkt.player.packetDispatch.sendLogout()
}
is Packet.TrackingCameraPos -> {
//TODO Refactor the player monitor to be actually useful and log this
}
is Packet.TrackingMouseClick -> {
//TODO see above todo
}
}
}
private fun processSlotSwitch(pkt: Packet) {
//TODO refactor this to function as callbacks to InterfaceListeners, e.g. onSlotSwitch
//TODO See above TODO, I hate Arios. This was hardcoded in the packet decoder.
if (pkt is Packet.SlotSwitchMultiComponent) {
if (pkt.destIface == 762) {
if (pkt.destChild == 73) {
val container = pkt.player.bank
switchItem(pkt.sourceSlot, pkt.destSlot, container, pkt.player.bank.isInsertItems, pkt.player)
} else {
val tabIndex = BankContainer.getArrayIndex(pkt.destChild)
if (tabIndex > -1) {
val secondSlot =
if (tabIndex == 10) pkt.player.bank.freeSlot() else pkt.player.bank.tabStartSlot[tabIndex] + pkt.player.bank.getItemsInTab(tabIndex)
val inSlot: Item = pkt.player.bank.get(pkt.sourceSlot)
if (secondSlot == -1 && pkt.player.bank.remove(inSlot)) {
pkt.player.bank.add(inSlot)
return
}
val childId = pkt.player.bank.getTabByItemSlot(pkt.sourceSlot)
if (secondSlot > pkt.sourceSlot) {
pkt.player.bank.insert(pkt.sourceSlot, secondSlot - 1)
} else if (pkt.sourceSlot > secondSlot) {
pkt.player.bank.insert(pkt.sourceSlot, secondSlot)
}
pkt.player.bank.increaseTabStartSlots(tabIndex)
pkt.player.bank.decreaseTabStartSlots(childId)
pkt.player.bank.setTabConfigurations()
return
}
}
return
} else {
switchItem(pkt.sourceSlot, pkt.destSlot, pkt.player.inventory, false, pkt.player)
}
}
else if (pkt is Packet.SlotSwitchSingleComponent) {
val container = if (pkt.iface == 762) pkt.player.bank else pkt.player.inventory
switchItem(pkt.sourceSlot, pkt.destSlot, container, pkt.isInsert, pkt.player)
}
}
private fun processComponentUseWith(pkt: Packet) {
val iface: Int
val child: Int
val target: Node
val targetId: Int
val player: Player
val type: Int
if (pkt is Packet.ComponentGroundItemAction) {
player = pkt.player
child = pkt.child
iface = pkt.iface
target = GroundItemManager.get(pkt.itemId, Location.create(pkt.x, pkt.y, player.location.z), player) ?: return sendClearMinimap(player).also { sendMessage(player, "Too late!") }
targetId = pkt.itemId
type = SpellListener.GROUND_ITEM
}
else if (pkt is Packet.ComponentPlayerAction) {
player = pkt.player
child = pkt.child
iface = pkt.iface
target = Repository.players[pkt.otherIndex] ?: return sendClearMinimap(player)
targetId = target.id
type = SpellListener.PLAYER
}
else if (pkt is Packet.ComponentSceneryAction) {
player = pkt.player
child = pkt.child
iface = pkt.iface
target = RegionManager.getObject(player.location.z, pkt.x, pkt.y) ?: return sendClearMinimap(player)
targetId = pkt.sceneryId
type = SpellListener.OBJECT
}
else if (pkt is Packet.ComponentNpcAction) {
if (pkt.npcIndex !in 1 until ServerConstants.MAX_NPCS)
return sendClearMinimap(pkt.player)
player = pkt.player
child = pkt.child
iface = pkt.iface
target = Repository.npcs[pkt.npcIndex] ?: return sendClearMinimap(player)
targetId = target.id
type = SpellListener.NPC
}
else {
if (pkt !is Packet.ComponentItemAction) return
player = pkt.player
child = pkt.child
iface = pkt.iface
target = player.inventory[pkt.slot] ?: return
targetId = pkt.itemId
type = SpellListener.ITEM
}
if (targetId != target.id)
return
if (player.getAttribute("magic:delay", -1) > GameWorld.ticks)
return
val book = SpellUtils.getBookFromInterface(iface)
if (book != "none")
SpellListeners.run(child, type, book, player, target)
when (iface) {
430,192,193 -> MagicSpell.castSpell(player, SpellBookManager.SpellBook.forInterface(iface), child, target)
662 -> {
if (player.familiarManager.hasFamiliar())
player.familiarManager.familiar.executeSpecialMove(FamiliarSpecial(target, iface, child, target as? Item))
else
player.sendMessage("You don't have a familiar.")
}
}
}
private fun processWalkPacket(pkt: Packet) {
val player: Player
val x: Int
val y: Int
val isRunning: Boolean
if (pkt is Packet.WorldspaceWalk) {
player = pkt.player
x = pkt.destX
y = pkt.destY
isRunning = pkt.isRun
}
else if (pkt is Packet.InteractWalk) {
player = pkt.player
x = pkt.destX
y = pkt.destY
isRunning = pkt.isRun
}
else {
if (pkt !is Packet.MinimapWalk) return
player = pkt.player
x = pkt.destX
y = pkt.destY
isRunning = pkt.isRun
//there's more data in this packet, we're just not using it
}
var canWalk = !player.locks.isMovementLocked
if (canWalk && player.interfaceManager.isOpened && !player.interfaceManager.opened.definition.isWalkable)
canWalk = canWalk && player.interfaceManager.close()
if (canWalk && player.interfaceManager.hasChatbox() && !player.interfaceManager.chatbox.definition.isWalkable)
player.interfaceManager.closeChatbox()
if (!canWalk || !player.dialogueInterpreter.close()) {
player.debug("[WALK ACTION]-- NO HANDLE: PLAYER LOCKED OR INTERFACES SAY NO")
return sendClearMinimap(player)
}
if (pkt is Packet.InteractWalk) {
//Arios did this, so we're replicating it for now. Probably wrong.
player.walkingQueue.isRunning = isRunning
return
}
player.face(null)
player.faceLocation(null)
player.pulseManager.run(object : MovementPulse(player, Location.create(x,y,player.location.z), isRunning) {
override fun pulse(): Boolean {
if (isRunning)
player.walkingQueue.isRunning = false
return true
}
})
}
private fun processCloseIface(pkt: Packet.CloseIface) {
val player = pkt.player
player.interfaceManager.close()
if (player.getAttribute<Boolean>("logging_in") != null) {
GameWorld.Pulser.submit(object : Pulse() {
override fun pulse(): Boolean {
player.removeAttribute("logging_in")
LoginConfiguration.configureGameWorld(player)
return true
}
})
}
if (player.getAttribute<Any?>("worldMap:viewing") != null) {
player.removeAttribute("worldMap:viewing")
player.packetDispatch.sendWindowsPane(if (player.interfaceManager.isResizable) 746 else 548, 2)
player.unlock()
}
}
private fun processIfAction(pkt: Packet.IfAction) {
val player = pkt.player
player.debug("[IF ACTION]--------------------------")
player.debug("Iface: ${pkt.iface}, Button: ${pkt.child}")
player.debug("Slot: ${pkt.slot}, ItemID: ${pkt.itemId}")
player.debug("RCM Index: ${pkt.optIndex}, Op: ${pkt.opcode}")
player.debug("-------------------------------------")
if (player.dialogueInterpreter.dialogue != null && pkt.opcode != 132 && pkt.iface != 64)
player.dialogueInterpreter.close()
if (player.locks.isComponentLocked)
return
if (player.zoneMonitor.clickButton(pkt.iface, pkt.child, pkt.slot, pkt.itemId, pkt.opcode))
return
val c = player.interfaceManager.getComponent(pkt.iface) ?: return
if (c.isHidden)
return
val plugin = c.plugin
player.dispatch(ButtonClickEvent(c.id, pkt.child))
if (!InterfaceListeners.run(player, c, pkt.opcode, pkt.child, pkt.slot, pkt.itemId))
plugin?.handle(player, c, pkt.opcode, pkt.child, pkt.slot, pkt.itemId)
}
private fun processUseWith(pkt: Packet) {
val node: Node
var childNode: Node? = null
val item: Item
val itemId: Int
val nodeId: Int
val type: IntType
val player: Player
if (pkt is Packet.UseWithNpc) {
val container = getLikelyContainerForIface(pkt.player, pkt.iface) ?: return sendClearMinimap(pkt.player)
itemId = pkt.itemId
val itemSlot = pkt.slot
item = container[itemSlot] ?: return sendClearMinimap(pkt.player)
node = Repository.npcs[pkt.npcIndex] ?: return sendClearMinimap(pkt.player)
childNode = node.getShownNPC(pkt.player)
nodeId = node.id
type = IntType.NPC
player = pkt.player
}
else if (pkt is Packet.UseWithScenery) {
item = pkt.player.inventory[pkt.slot] ?: return sendClearMinimap(pkt.player)
node = RegionManager.getObject(pkt.player.location.z, pkt.x, pkt.y) ?: return sendClearMinimap(pkt.player)
childNode = node.asScenery().getChild(pkt.player)
itemId = pkt.itemId
nodeId = node.id
type = IntType.SCENERY
player = pkt.player
}
else if (pkt is Packet.UseWithItem) {
val containerUsed = getLikelyContainerForIface(pkt.player, pkt.usedIface) ?: return
val containerWith = getLikelyContainerForIface(pkt.player, pkt.usedWithIface) ?: return
item = containerUsed[pkt.usedSlot] ?: return
node = containerWith[pkt.usedWithSlot] ?: return
itemId = pkt.usedId
nodeId = pkt.usedWithId
type = IntType.ITEM
player = pkt.player
} else if (pkt is Packet.UseWithGroundItem) {
val container = getLikelyContainerForIface(pkt.player, pkt.iface) ?: return
item = container[pkt.slot]
itemId = pkt.usedId
node = GroundItemManager.get(pkt.withId, Location.create(pkt.x, pkt.y, pkt.player.location.z), pkt.player)
nodeId = pkt.withId
type = IntType.GROUNDITEM
player = pkt.player
} else {
if (pkt !is Packet.UseWithPlayer) return
val container = getLikelyContainerForIface(pkt.player, pkt.iface) ?: return sendClearMinimap(pkt.player)
item = container[pkt.slot] ?: return sendClearMinimap(pkt.player)
itemId = pkt.itemId
node = Repository.players[pkt.otherIndex] ?: return sendClearMinimap(pkt.player)
type = IntType.PLAYER
nodeId = node.id
player = pkt.player
}
if (item.id != itemId)
return sendClearMinimap(player)
if (node.id != nodeId)
return sendClearMinimap(player)
if (player.zoneMonitor.useWith(item, node))
return
if (InteractionListeners.run(item, node, type, player))
return
if (childNode != null && childNode.id != node.id) {
if (InteractionListeners.run(item, childNode, type, player))
return
}
val flipped = type == IntType.ITEM && item.id < node.id
val event = if (flipped)
NodeUsageEvent(player, 0, node, item)
else
NodeUsageEvent(player, 0, item, childNode ?: node)
if (PluginInteractionManager.handle(player, event))
return
UseWithHandler.run(event)
}
private fun processGroundItemAction(pkt: Packet.GroundItemAction) {
val item = GroundItemManager.get(pkt.id, Location.create(pkt.x, pkt.y, pkt.player.location.z), pkt.player)
val player = pkt.player
if (item == null) {
return sendClearMinimap(player)
}
val option = item.interaction[pkt.optIndex]
if (option == null) {
Interaction.handleInvalidInteraction(player, item, Option.NULL)
return sendClearMinimap(player)
}
if (PluginInteractionManager.handle(player, item, option))
return
if (InteractionListeners.run(item.id, IntType.GROUNDITEM, option.name, player, item))
return
if (InteractionListeners.run(item.id, IntType.ITEM, option.name, player, item))
return
item.interaction.handle(player, option)
}
private fun processPlayerAction(pkt: Packet.PlayerAction) {
val player = pkt.player
if (pkt.otherIndex !in 1 until ServerConstants.MAX_PLAYERS) {
return sendClearMinimap(player)
}
val other = Repository.players[pkt.otherIndex]
if (other == null || !other.isActive)
return sendClearMinimap(player)
val option = other.interaction[pkt.optIndex] ?: return sendClearMinimap(player)
if (!InteractionListeners.run(-1, IntType.PLAYER, option.name.lowercase(), player, other)) {
other.interaction.handle(player, option)
}
}
private fun processSceneryAction(pkt: Packet.SceneryAction) {
val player = pkt.player
var scenery = RegionManager.getObject(player.location.z, pkt.x, pkt.y, pkt.id)
var objId = pkt.id
//what follows is a series of hardcoded crimes against humanity
if (pkt.id == 6898)
scenery = Scenery(6898, Location(3219, 9618))
if (pkt.id == 6899)
scenery = Scenery(6899, Location(3221, 9618))
// Family crest levers don't have varps associated with them, so their state is validated with attributes
// instead, and they always appear as their down/odd variant in the server's map
if (objId in 2421..2426 && objId % 2 == 0) {
scenery = Scenery(objId - 1, Location(pkt.x, pkt.y, player.location.z))
objId -= 1
}
if (scenery == null || scenery.id != objId || !scenery.isActive) {
player.debug("[SCENERY INTERACT] NULL OR MISMATCH OR INACTIVE")
Interaction.handleInvalidInteraction(player, scenery, Option.NULL)
return sendClearMinimap(player)
}
val wrapperChild = scenery.getChild(player)
val option = wrapperChild.interaction[pkt.optIndex]
if (option == null) {
player.debug("[SCENERY INTERACT] NULL OPTION")
Interaction.handleInvalidInteraction(player, scenery, Option.NULL)
return sendClearMinimap(player)
}
val hasWrapper = wrapperChild.id != scenery.id
player.debug("[SCENERY INTERACT]------------------------------")
player.debug("ID: ${wrapperChild.id}, Option: ${option.name}[${option.index}]")
player.debug("Loc: ${scenery.location}, Dir: ${scenery.direction}")
if (hasWrapper) {
player.debug("WrapperID: ${scenery.id}, ${scenery.definition.configFile?.let { "Varbit: ${it.id}"} ?: "Varp: ${scenery.definition.configId}"}")
}
player.debug("------------------------------------------------")
if (InteractionListeners.run(wrapperChild.id, IntType.SCENERY, option.name, player, wrapperChild))
return
if (PluginInteractionManager.handle(player, wrapperChild))
return
wrapperChild.interaction.handle(player, option)
}
private fun processNpcAction(pkt: Packet.NpcAction) {
if (pkt.npcIndex !in 1 until ServerConstants.MAX_NPCS)
return sendClearMinimap(pkt.player)
val npc = Repository.npcs[pkt.npcIndex] ?: return sendClearMinimap(pkt.player)
val wrapperChild = npc.getShownNPC(pkt.player)
val option = wrapperChild.interaction[pkt.optIndex]
if (option == null) {
Interaction.handleInvalidInteraction(pkt.player, npc, Option.NULL)
return sendClearMinimap(pkt.player)
}
val hasWrapper = wrapperChild.id != npc.id
pkt.player.debug("[NPC INTERACT]-------------------")
pkt.player.debug("ID: ${wrapperChild.id}, Index: ${pkt.npcIndex}")
pkt.player.debug("Option: ${option.name}[${option.index}]")
pkt.player.debug("SpawnLoc: ${npc.properties.spawnLocation}")
if (hasWrapper) {
pkt.player.debug("WrapperID: ${npc.id}, Varbit: ${npc.definition.configFileId}")
}
pkt.player.debug("---------------------------------")
if (InteractionListeners.run(wrapperChild.id, IntType.NPC,option.name,pkt.player,npc))
return
if (PluginInteractionManager.handle(pkt.player, wrapperChild, option))
return
npc.interaction.handle(pkt.player, option)
}
private fun processItemAction(pkt: Packet.ItemAction) {
val container = getLikelyContainerForIface(pkt.player, pkt.iface) ?: return
if (pkt.slot !in 0 until container.capacity())
return
val item = container.get(pkt.slot) ?: return
if (item.id != pkt.itemId)
return
val option = item.interaction[pkt.optIndex] ?: return
if (pkt.player.locks.isInteractionLocked)
return
item.interaction.handleItemOption(pkt.player, option, container)
pkt.player.debug("[ITEM INTERACT] ID: ${item.id}, Slot: ${pkt.slot}, Opt: ${option.name}")
}
private fun getLikelyContainerForIface(player: Player, iface: Int) : Container? {
return when (iface) {
Components.INVENTORY_149 -> player.inventory
Components.BANK_V2_MAIN_762 -> player.bank
Components.EQUIP_SCREEN2_667 -> player.equipment
else -> null
}
}
private fun sendClearMinimap(player: Player) {
PacketRepository.send(ClearMinimapFlag::class.java, PlayerContext(player))
}
fun switchItem(slot: Int, secondSlot: Int, container: Container?, insert: Boolean, player: Player) {
if (container == null || slot < 0 || slot >= container.toArray().size || secondSlot < 0 || secondSlot >= container.toArray().size) {
return
}
val item = container[slot]
val second = container[secondSlot]
if (player.interfaceManager.hasChatbox() && !getAttribute(player, "close_c_", false)) {
player.interfaceManager.closeChatbox()
switchItem(secondSlot, slot, container, insert, player)
container.refresh()
return
}
if (item == null) {
return
}
if (!insert) {
container.replace(second, slot, false)
container.replace(item, secondSlot, false)
item.index = secondSlot
if (second != null) {
second.index = slot
}
} else {
container.insert(slot, secondSlot, false)
}
container.refresh()
}
}
@@ -0,0 +1,93 @@
package core.net.packet;
import core.net.packet.out.GrandExchangePacket;
import core.net.packet.out.*;
import core.tools.SystemLogger;
import java.util.HashMap;
import java.util.Map;
/**
* The packet repository.
* @author Emperor
*/
public final class PacketRepository {
/**
* The outgoing packets mapping.
*/
public final static Map<Class<?>, OutgoingPacket<? extends Context>> OUTGOING_PACKETS = new HashMap<>();
/**
* Populate the mappings.
*/
static {
OUTGOING_PACKETS.put(LoginPacket.class, new LoginPacket()); //
OUTGOING_PACKETS.put(UpdateSceneGraph.class, new UpdateSceneGraph()); //
OUTGOING_PACKETS.put(WindowsPane.class, new WindowsPane()); //
OUTGOING_PACKETS.put(Interface.class, new Interface()); //
OUTGOING_PACKETS.put(SkillLevel.class, new SkillLevel()); //
OUTGOING_PACKETS.put(Config.class, new Config()); //
OUTGOING_PACKETS.put(AccessMask.class, new AccessMask()); //
OUTGOING_PACKETS.put(GameMessage.class, new GameMessage()); //
OUTGOING_PACKETS.put(RunScriptPacket.class, new RunScriptPacket()); //
OUTGOING_PACKETS.put(RunEnergy.class, new RunEnergy()); //
OUTGOING_PACKETS.put(ContainerPacket.class, new ContainerPacket()); //
OUTGOING_PACKETS.put(StringPacket.class, new StringPacket()); //
OUTGOING_PACKETS.put(Logout.class, new Logout()); //
OUTGOING_PACKETS.put(CloseInterface.class, new CloseInterface()); //
OUTGOING_PACKETS.put(AnimateInterface.class, new AnimateInterface()); //
OUTGOING_PACKETS.put(DisplayModel.class, new DisplayModel()); //
OUTGOING_PACKETS.put(InterfaceConfig.class, new InterfaceConfig()); //
OUTGOING_PACKETS.put(PingPacket.class, new PingPacket()); //
OUTGOING_PACKETS.put(UpdateAreaPosition.class, new UpdateAreaPosition()); //
OUTGOING_PACKETS.put(ConstructScenery.class, new ConstructScenery()); //
OUTGOING_PACKETS.put(ClearScenery.class, new ClearScenery()); //
OUTGOING_PACKETS.put(HintIcon.class, new HintIcon()); //
OUTGOING_PACKETS.put(ClearMinimapFlag.class, new ClearMinimapFlag()); //
OUTGOING_PACKETS.put(InteractionOption.class, new InteractionOption()); //
OUTGOING_PACKETS.put(SetWalkOption.class, new SetWalkOption()); //
OUTGOING_PACKETS.put(MinimapState.class, new MinimapState()); //
OUTGOING_PACKETS.put(ConstructGroundItem.class, new ConstructGroundItem()); //
OUTGOING_PACKETS.put(ClearGroundItem.class, new ClearGroundItem()); //
OUTGOING_PACKETS.put(RepositionChild.class, new RepositionChild()); //
OUTGOING_PACKETS.put(PositionedGraphic.class, new PositionedGraphic()); //
OUTGOING_PACKETS.put(SystemUpdatePacket.class, new SystemUpdatePacket()); //
OUTGOING_PACKETS.put(CameraViewPacket.class, new CameraViewPacket()); //
OUTGOING_PACKETS.put(MusicPacket.class, new MusicPacket()); //
OUTGOING_PACKETS.put(AudioPacket.class, new AudioPacket()); //
OUTGOING_PACKETS.put(GrandExchangePacket.class, new GrandExchangePacket()); //
OUTGOING_PACKETS.put(BuildDynamicScene.class, new BuildDynamicScene()); //
OUTGOING_PACKETS.put(AnimateObjectPacket.class, new AnimateObjectPacket()); //
OUTGOING_PACKETS.put(ClearRegionChunk.class, new ClearRegionChunk()); //
OUTGOING_PACKETS.put(ContactPackets.class, new ContactPackets()); //
OUTGOING_PACKETS.put(CommunicationMessage.class, new CommunicationMessage()); //
OUTGOING_PACKETS.put(UpdateClanChat.class, new UpdateClanChat()); //
OUTGOING_PACKETS.put(UpdateGroundItemAmount.class, new UpdateGroundItemAmount()); //
OUTGOING_PACKETS.put(WeightUpdate.class, new WeightUpdate()); //
OUTGOING_PACKETS.put(UpdateRandomFile.class, new UpdateRandomFile()); //
OUTGOING_PACKETS.put(InstancedLocationUpdate.class, new InstancedLocationUpdate()); //
OUTGOING_PACKETS.put(CSConfigPacket.class, new CSConfigPacket()); //
OUTGOING_PACKETS.put(Varbit.class, new Varbit());
OUTGOING_PACKETS.put(VarcUpdate.class, new VarcUpdate());
}
/**
* Sends a new packet.
* @param clazz The class of the outgoing packet to send.
* @param context The context.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void send(Class<? extends OutgoingPacket> clazz, Context context) {
if(context.getPlayer().getSession() == null) return;
OutgoingPacket p = OUTGOING_PACKETS.get(clazz);
if (p == null) {
SystemLogger.logErr(PacketRepository.class, "Invalid outgoing packet [handler=" + clazz + ", context=" + context + "].");
return;
}
if(!context.getPlayer().isArtificial()) {
PacketWriteQueue.handle(p, context);
}
}
}
@@ -0,0 +1,91 @@
package core.net.packet
import core.api.TickListener
import core.net.packet.out.*
import core.tools.SystemLogger
import java.lang.IndexOutOfBoundsException
import java.util.*
import java.util.concurrent.locks.ReentrantLock
import kotlin.NoSuchElementException
class PacketWriteQueue : TickListener {
override fun tick() {
flush()
}
companion object {
private val queueLock = ReentrantLock()
private val packetsToQueue = LinkedList<QueuedPacket<*>?>()
private val packetsToWrite = LinkedList<QueuedPacket<*>?>()
@JvmStatic
fun <T> handle(packet: OutgoingPacket<T>, context: T) {
when (packet) {
//Dynamic packets need to be sent immediately
is UpdateSceneGraph,
is BuildDynamicScene,
is InstancedLocationUpdate,
is ClearRegionChunk -> packet.send(context)
//Rest get queued up and sent at the end of the tick (authentic)
else -> push(packet, context)
}
}
@JvmStatic
fun <T> push(packet: OutgoingPacket<T>, context: T) {
if (queueLock.isHeldByCurrentThread)
packetsToQueue.add(QueuedPacket(packet, context))
else
packetsToWrite.add(QueuedPacket(packet, context))
}
@JvmStatic
fun flush() {
queueLock.lock()
var hasEnded = false
while (!hasEnded) {
try {
val packet = packetsToWrite.pop()
write(packet?.out ?: continue, packet.context ?: continue)
} catch (e: NoSuchElementException) {
hasEnded = true
}
}
if (packetsToWrite.isNotEmpty()) {
SystemLogger.logWarn(this::class.java, "Packet queue was NOT empty! Remaining packets: ${packetsToWrite.size}")
try {
for (pkt: QueuedPacket<*>? in packetsToWrite) SystemLogger.logWarn(this::class.java, "${pkt?.out?.javaClass?.simpleName ?: "NULL"} <- ${pkt?.context ?: "NULL"}")
} catch (ignored: NullPointerException) {
//do nothing, we don't care, this can happen when everything is working as intended.
} catch (ignored: IndexOutOfBoundsException) {
//do nothing, we don't care, this can happen when everything is working as intended.
} catch (e: Exception) {
e.printStackTrace()
} finally {
packetsToWrite.clear()
}
}
queueLock.unlock()
while (packetsToQueue.isNotEmpty()) {
packetsToWrite.add(packetsToQueue.pop())
}
}
@Suppress("UNCHECKED_CAST")
fun <T> write(out: OutgoingPacket<*>, context: T) {
val pack = out as? OutgoingPacket<T>
val ctx = context as? T
if (pack == null || ctx == null) {
SystemLogger.logWarn(this::class.java, "Failed packet casting")
return
}
pack.send(ctx)
}
}
}
class QueuedPacket<T>(val out: OutgoingPacket<T>, val context: T)
@@ -0,0 +1,320 @@
package core.net.packet
import content.global.skill.slayer.SlayerManager
import core.cache.Cache
import core.cache.def.impl.DataMap
import core.cache.def.impl.ItemDefinition
import core.cache.def.impl.NPCDefinition
import core.cache.misc.buffer.ByteBufferUtils
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.system.task.Pulse
import core.game.world.update.flag.context.ChatMessage
import core.game.world.update.flag.player.ChatFlag
import proto.management.ClanMessage
import core.game.world.GameWorld.Pulser
import core.net.packet.`in`.QCPacketType
import core.worker.ManagementEvents.publish
import java.nio.ByteBuffer
import java.util.*
/**
* Handles the sending of quick chat messages and string-replacement for specific messages.
* @author Ceikry
*/
object QCRepository {
private val quickChatIndex = Cache.getIndexes()[24]
/**
* The entry method that connects to the other more specific methods
*/
@JvmStatic
fun sendQC(player: Player?, multiplier: Int?, offset: Int?, packetType: QCPacketType, selection_a_index: Int, selection_b_index: Int, forClan: Boolean){
val index = getIndex(offset, multiplier)
player?.setAttribute("qc_offset", offset) //Slapping this in an attribute because it's only useful for one or two messages
val qcString = when(packetType){
QCPacketType.SINGLE -> getSingleQC(index, selection_a_index)
QCPacketType.DOUBLE -> getDoubleQC(index, selection_a_index, selection_b_index)
QCPacketType.STANDARD -> getStandardQC(player, index)
else -> ""
}
if(forClan) {
val builder = ClanMessage.newBuilder()
builder.sender = player!!.name
builder.clanName = player.communication.clan.owner.lowercase(Locale.getDefault()).replace(" ", "_")
builder.message = qcString
builder.rank = player.details.rights.ordinal
publish(builder.build())
} else {
val ctx = ChatMessage(player!!, qcString, 0, qcString.length)
ctx.isQuickChat = true
Pulser.submit(object : Pulse(0, player) {
override fun pulse(): Boolean {
player.updateMasks.register(ChatFlag(ctx))
return true
}
})
}
}
/**
* For standard quick chat messages with no string replacements
*/
@JvmStatic
fun getStandardQC(player: Player?, index: Int): String{
var qcString = getQCString(index)
//XP to next level
if (qcString.contains("to get my next")) {
val split = qcString.split(" ")
val skillName = split[split.size - 2]
val skill = Skills.getSkillByName(skillName.toUpperCase())
val playerXP = player?.skills?.getExperience(skill)
val playerLevel = player?.skills?.getStaticLevel(skill)
val nextXP = player?.skills?.getExperienceByLevel(playerLevel?.plus(1) ?: 1)
qcString = qcString.replace("<", "${(nextXP?.minus(playerXP ?: 0.0) ?: 0.0).toInt()}")
}
//My X level is
else if (qcString.contains("level is")) {
val skillName = qcString.split(" ")[1]
val skill = Skills.getSkillByName(skillName.toUpperCase())
val level = player?.skills?.getStaticLevel(skill)
qcString = qcString.replace("<", level.toString())
}
//My current slayer assignment is
else if(qcString.contains("My current Slayer assignment is")){
val amount = SlayerManager.getInstance(player!!).amount
val taskName = NPCDefinition.forId(SlayerManager.getInstance(player!!).task?.ids?.get(0) ?: 0).name.toLowerCase()
if(amount ?: 0 > 0){
qcString = qcString.replace("complete", "$amount $taskName")
}
}
return qcString
}
/**
* For Single-replacement quick chat messages with one replacement selection
*/
@JvmStatic
fun getSingleQC(index: Int, selectionIndex: Int): String{
var qcString = getQCString(index)
//Messages that include items
if ((qcString.contains("item") || qcString.contains("Can I buy your") || qcString.contains("What is the best world to buy") || qcString.contains("What is the best world to sell") || qcString.contains("Would you like to borrow") || qcString.contains("Could I please borrow") ) && qcString.contains("<") ){
val itemName = ItemDefinition.forId(selectionIndex).name
qcString = qcString.replace("<", itemName)
}
//Loan duration
else if (qcString.contains("I'd like the loan duration to be")){
qcString = qcString.replace("<", getFromMap(1642, selectionIndex))
}
//Go to agility course / Try training your agility at
else if (qcString.contains("Let's go to Agility course:") || qcString.contains("Try training your Agility at:")){
qcString = qcString.replace("<", getFromMap(1505, selectionIndex))
}
//Try training on
else if(qcString.contains("Try training on")){
qcString = qcString.replace("<", getFromMap(1524, selectionIndex))
}
//Try ranging
else if(qcString.contains("Try ranging")){
qcString = qcString.replace("<", getFromMap(1525, selectionIndex))
}
//flat-pack
else if(qcString.contains("flat-pack")){
qcString = qcString.replace("<", getFromMap(1490, selectionIndex))
}
//Cooking
else if(qcString.contains("I'm cooking") || qcString.contains("Would you please cook me") || qcString.contains("Try cooking")){
qcString = qcString.replace("<", getFromMap(1492, selectionIndex))
}
//Crafting
else if(qcString.contains("I am crafting") || qcString.contains("Try crafting") || qcString.contains("Would you please craft me")){
qcString = qcString.replace("<", getFromMap(1493, selectionIndex))
}
//Farming
else if(qcString.contains("I'm growing crop") || qcString.contains("Try growing crop")){
qcString = qcString.replace("<", getFromMap(1494, selectionIndex))
}
//I'm burning logs...
else if(qcString.contains("I'm burning logs")){
qcString = qcString.replace("<", getFromMap(1495, selectionIndex))
}
//Try burning logs at
else if(qcString.contains("Try burning logs at")){
qcString = qcString.replace("<", getFromMap(1544, selectionIndex))
}
//Fishing
else if(qcString.contains("I'm fishing") || qcString.contains("Would you please fish me") || qcString.contains("Try fishing for")){
qcString = qcString.replace("<", getFromMap(1491, selectionIndex))
}
//Fletching
else if(qcString.contains("I'm fletching") || qcString.contains("Try fletching") || qcString.contains("Would you please fetch me")){
qcString = qcString.replace("<", getFromMap(1496, selectionIndex))
}
//Herblore
else if(qcString.contains("I'm mixing potion") || qcString.contains("Would you please mix me a potion") || qcString.contains("Try mixing potion")){
qcString = qcString.replace("<", getFromMap(1526, selectionIndex))
}
//Herblore secondaries
else if(qcString.contains("Where can I get the secondary ingredient")){
qcString = qcString.replace("<", getFromMap(1516, selectionIndex))
}
//Hunter
else if(qcString.contains("Would you please hunt me") || qcString.contains("Try hunting")){
qcString = qcString.replace("<", getFromMap(1543, selectionIndex))
}
//Casting spell
else if(qcString.contains("I'm casting spell") || qcString.contains("Would you please cast")){
qcString = qcString.replace("<", getFromMap(1527, selectionIndex))
}
//Spellbook
else if(qcString.contains("I am on spell book")){
qcString = qcString.replace("<", getFromMap(1528, selectionIndex))
}
//Mining ore
else if(qcString.contains("I'm mining ore")){
qcString = qcString.replace("<", getFromMap(1529, selectionIndex))
}
//Pickaxe using
else if(qcString.contains("I'm using a pick")){
qcString = qcString.replace("<", getFromMap(1531, selectionIndex))
}
//Try mining at
else if(qcString.contains("Try mining at")){
qcString = qcString.replace("<", getFromMap(1533, selectionIndex))
}
//Use your prayer
else if(qcString.contains("Use your prayer")){
qcString = qcString.replace("<", getFromMap(1534, selectionIndex))
}
//i'm going to craft rune
else if(qcString.contains("I'm going to craft rune")){
qcString = qcString.replace("<", getFromMap(1535, selectionIndex))
}
//try crafting runes at
else if(qcString.contains("Try crafting runes at")){
qcString = qcString.replace("<", getFromMap(1536, selectionIndex))
}
//Slayer master in
else if(qcString.contains("You should use the Slayer master in")){
qcString = qcString.replace("<", getFromMap(2139, selectionIndex))
}
//Spare slayer equipment
else if(qcString.contains("Do you have spare Slayer equipment")){
qcString = qcString.replace("<", getFromMap(1538, selectionIndex))
}
//familiars
else if(qcString.contains("I like the familiar") || qcString.contains("I can summon up to")){
qcString = qcString.replace("<", getFromMap(1539, selectionIndex))
}
//charm droppers
else if(qcString.contains("Good charm droppers are")){
qcString = qcString.replace("<", getFromMap(1499, selectionIndex))
}
//Thieving
else if(qcString.contains("Try thieving from")){
qcString = qcString.replace("<", getFromMap(1540, selectionIndex))
}
//Axe made of
else if(qcString.contains("I'm using a woodcutting axe made")){
qcString = qcString.replace("<", getFromMap(1532, selectionIndex))
}
//Try training woodcutting at
else if(qcString.contains("Try training Woodcutting at")){
qcString = qcString.replace("<", getFromMap(1500, selectionIndex))
}
//Nice level in
else if(qcString.contains("Nice level in")){
qcString = qcString.replace("<", getFromMap(1517, selectionIndex))
}
return qcString
}
fun getFromMap(map: Int, index: Int): String
{
return DataMap.get(map).getString(index)
}
/**
* For Double-replacement quick chat messages with 2 selection replacements.
*/
@JvmStatic
fun getDoubleQC(index: Int, selection_a_index: Int, selection_b_index: Int): String{
var qcString = getQCString(index)
//Giving directions: That is _ of _
if(qcString.contains("That is < of <")){
qcString = qcString.replaceFirst("<", getFromMap(1502, selection_a_index))
qcString = qcString.replaceFirst("<", getFromMap(1504, selection_b_index))
}
//Smithing
else if(qcString.contains("I am smithing") || qcString.contains("Try smithing") || qcString.contains("Would you please smith me")){
qcString = qcString.replaceFirst("<", getFromMap(1530, selection_a_index))
qcString = qcString.replaceFirst("<", getFromMap(1498, selection_b_index))
}
return qcString
}
private fun getIndex(offset: Int?, multiplier: Int?): Int{
offset ?: return 0
multiplier ?: return 0
//for some reason it subtracts only from 512 if it's negative (Jagex idk) or 256 if type is 0 but not from 768 if type is 3?
return if(offset >= 0) {
(256 * multiplier) + offset
}else {
when(multiplier){
0 -> 256 + offset
1 -> 512 + offset
2 -> 768 + offset
else -> 1024 + offset
}
}
}
private fun getQCString(index: Int): String{
return ByteBufferUtils.getString(ByteBuffer.wrap(quickChatIndex.getFileData(1, index)))
}
}
@@ -0,0 +1,125 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The access mask context.
* @author Emperor
*/
public class AccessMaskContext implements Context {
/**
* The player reference.
*/
private Player player;
/**
* The access mask id.
*/
private int id;
/**
* The child id.
*/
private int childId;
/**
* The interface id.
*/
private int interfaceId;
/**
* The offset.
*/
private int offset;
/**
* The length.
*/
private int length;
/**
* Construct a new {@code AccessMaskContext} {@code Object}.
* @param player
* @param id
* @param childId
* @param interfaceId
* @param offset
* @param length
*/
public AccessMaskContext(Player player, int id, int childId, int interfaceId, int offset, int length) {
this.player = player;
this.id = id;
this.childId = childId;
this.interfaceId = interfaceId;
this.offset = offset;
this.length = length;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Transforms this access mask context into a new instance with the player
* instance and id value changed.
* @param player The player to set.
* @param id The id to set.
* @return The access mask context.
*/
public AccessMaskContext transform(Player player, int id) {
return new AccessMaskContext(player, id, childId, interfaceId, offset, length);
}
/**
* Sets the player.
* @param player The player.
* @return This context instance.
*/
public Context setPlayer(Player player) {
this.player = player;
return this;
}
/**
* Get the access mask id.
* @return The access mask.
*/
public int getId() {
return id;
}
/**
* Get the child id.
* @return The child id.
*/
public int getChildId() {
return childId;
}
/**
* Get the interface id.
* @return The interface id.
*/
public int getInterfaceId() {
return interfaceId;
}
/**
* Get the offset.
* @return The offset.
*/
public int getOffset() {
return offset;
}
/**
* Get the length.
* @return The length.
*/
public int getLength() {
return length;
}
}
@@ -0,0 +1,74 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The animate interface context.
* @author Emperor
*/
public class AnimateInterfaceContext implements Context {
/**
* The player reference.
*/
private Player player;
/**
* The animation id.
*/
private int animationId;
/**
* The interface id.
*/
private int interfaceId;
/**
* The child id.
*/
private int childId;
/**
* Construct a new {@code AnimateInterfaceContext} {@code Object}.
* @param player The player reference.
* @param animationId The animation id.
* @param interfaceId The interface id.
* @param childId The child id.
*/
public AnimateInterfaceContext(Player player, int animationId, int interfaceId, int childId) {
this.player = player;
this.animationId = animationId;
this.interfaceId = interfaceId;
this.childId = childId;
}
/**
* Get the animation id.
* @return The animation id.
*/
public int getAnimationId() {
return animationId;
}
/**
* Get the interface id.
* @return The interface id.
*/
public int getInterfaceId() {
return interfaceId;
}
/**
* Get the child id.
* @return The child id.
*/
public int getChildId() {
return childId;
}
@Override
public Player getPlayer() {
return player;
}
}
@@ -0,0 +1,47 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.game.world.update.flag.context.Animation;
import core.net.packet.Context;
/**
* Represents the context of animating an object.
* @author Emperor
* @author 'Vexia
* @date 10/11/2013
*/
public class AnimateObjectContext implements Context {
/**
* Represents the player of the context.
*/
private final Player player;
/**
* Represents the animation to use.
*/
private final Animation animation;
/**
* Constructs a new {@code AnimateObjectContext} {@code Object}.
* @param player the player.
* @param animation the animation.
*/
public AnimateObjectContext(Player player, Animation animation) {
this.player = player;
this.animation = animation;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the animation.
* @return The animation.
*/
public Animation getAnimation() {
return animation;
}
}
@@ -0,0 +1,76 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.game.world.map.Location;
import core.net.packet.Context;
/**
* Handles the area position update packet context.
* @author Emperor
*/
public final class AreaPositionContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* The location.
*/
private final Location location;
/**
* The offset x-coordinate.
*/
private final int offsetX;
/**
* The offset y-coordinate.
*/
private final int offsetY;
/**
* Constructs a new {@code AreaPositionContext} {@code Object}.
* @param player The player.
* @param location The location.
* @param offsetX The offset x-coordinate.
* @param offsetY The offset y-coordinate.
*/
public AreaPositionContext(Player player, Location location, int offsetX, int offsetY) {
this.player = player;
this.location = location;
this.offsetX = offsetX;
this.offsetY = offsetY;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the location.
* @return The location.
*/
public Location getLocation() {
return location;
}
/**
* Gets the offsetX.
* @return The offsetX.
*/
public int getOffsetX() {
return offsetX;
}
/**
* Gets the offsetY.
* @return The offsetY.
*/
public int getOffsetY() {
return offsetY;
}
}
@@ -0,0 +1,71 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.net.packet.Context;
/**
* Represents the build item packet context, <br> which is used for
* construct/clear item outgoing packet.
* @author Emperor
*/
public final class BuildItemContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* The item to send.
*/
private final Item item;
/**
* The old item amount.
*/
private final int oldAmount;
/**
* Constructs a new {@code BuildObjectContext} {@code Object}.
* @param player The player
* @param item The item to send.
*/
public BuildItemContext(Player player, Item item) {
this(player, item, 0);
}
/**
* Constructs a new {@code BuildObjectContext} {@code Object}.
* @param player The player
* @param item The item to send.
* @param oldAmount The old item amount.
*/
public BuildItemContext(Player player, Item item, int oldAmount) {
this.player = player;
this.item = item;
this.oldAmount = oldAmount;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the item.
* @return The item.
*/
public Item getItem() {
return item;
}
/**
* Gets the oldAmount.
* @return The oldAmount.
*/
public int getOldAmount() {
return oldAmount;
}
}
@@ -0,0 +1,47 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.game.node.scenery.Scenery;
import core.net.packet.Context;
/**
* Represents the build object packet context, <br> which is used for
* construct/clear object outgoing packet.
* @author Emperor
*/
public final class BuildSceneryContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* The list of scenerys to send.
*/
private final Scenery scenery;
/**
* Constructs a new {@code BuildObjectContext} {@code Object}.
* @param player The player
* @param scenery the scenery to send.
*/
public BuildSceneryContext(Player player, Scenery scenery) {
this.player = player;
this.scenery = scenery;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the gameObject.
* @return The gameObject.
*/
public Scenery getScenery() {
return scenery;
}
}
@@ -0,0 +1,84 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The context for ClientScript configurations.
*
* @author Torchic
*/
public class CSConfigContext implements Context {
/**
* The player reference.
*/
private Player player;
/**
* The value
*/
private int value;
/**
* The id.
*/
private int id;
private final Object[] parameters;
private final String types;
/**
* Constructs a new {@Code CSConfigContext} {@Code Object}
* @param player The player.
* @param id The id.
* @param value The config value.
* @param parameters
* @param types
*/
public CSConfigContext(Player player, int id, int value, String types, Object[] parameters) {
this.player = player;
this.value = value;
this.id = id;
this.parameters = parameters;
this.types = types;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Sets the player.
* @param player The player.
* @return This context instance.
*/
public Context setPlayer(Player player) {
this.player = player;
return this;
}
/**
* Get the config value.
* @return The config value.
*/
public int getValue() {
return value;
}
/**
* Get the config id.
* @return The config id.
*/
public int getId() {
return id;
}
public String getTypes() {
return types;
}
public Object[] getParameters() {
return parameters;
}
}
@@ -0,0 +1,177 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* Represents a camera context.
* @author Emperor
*/
public final class CameraContext implements Context {
/**
* Represents the camera types used for determining which packet to send.
* @author Emperor
*/
public static enum CameraType {
POSITION(154),
ROTATION(125),
SET(187), SHAKE(27), RESET(24);
/**
* The opcode.
*/
private final int opcode;
/**
* Constructs a new {@code CameraContext} {@Code Object}.
* @param opcode The opcode.
*/
private CameraType(int opcode) {
this.opcode = opcode;
}
/**
* Gets the packet opcode.
* @return The opcode.
*/
public int opcode() {
return opcode;
}
}
/*
* rotateCamera(Player p, int x, int y, int z, int angle) { MessageBuilder
* bldr = new MessageBuilder(26); bldr.writeByte(y); //idk
* bldr.writeByte(x); //idk bldr.writeLEShortA(z >>> 2); //idk
* bldr.writeByte(0); // speed :S bldr.writeByteS(100);
*/
/**
* The player.
*/
private final Player player;
/**
* The camera type.
*/
private final CameraType type;
/**
* x The (local) x-coordinate of the camera.
*/
private final int x;
/**
* The (local) y-coordinate of the camera.
*/
private final int y;
/**
* The height.
*/
private final int height;
/**
* The zoom speed.
*/
private final int zoomSpeed;
/**
* The speed.
*/
private final int speed;
/**
* Constructs a new {@code CameraContext} {@Code Object}.
* @param player The player.
* @param type The camera type.
* @param x The x-coordinate of the camera.
* @param y The y-coordinate of the camera.
* @param height The height.
* @param zoomSpeed The zoom speed.
* @param speed The speed.
*/
public CameraContext(Player player, CameraType type, int x, int y, int height, int speed, int zoomSpeed) {
this.player = player;
this.type = type;
this.x = x;
this.y = y;
this.height = height;
this.speed = speed;
this.zoomSpeed = zoomSpeed;
}
/**
* Method used to transform a camera context.
* @param x the x.
* @param y the y.
* @return the new context.
*/
public CameraContext transform(final Player player, final int x, final int y) {
return new CameraContext(player, type, this.x + x, this.y + y, height, speed, zoomSpeed);
}
/**
* Method used to transform the context.
* @param heightOffset the height offset.
* @return the new context.
*/
public CameraContext transform(final int heightOffset) {
return new CameraContext(player, type, x, y, height + heightOffset, speed, zoomSpeed);
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the type.
* @return The type.
*/
public CameraType getType() {
return type;
}
/**
* Gets the x.
* @return The x.
*/
public int getX() {
return x;
}
/**
* Gets the y.
* @return The y.
*/
public int getY() {
return y;
}
/**
* Gets the height.
* @return The height.
*/
public int getHeight() {
return height;
}
/**
* Gets the speed.
* @return The speed.
*/
public int getSpeed() {
return speed;
}
/**
* Gets the zoomSpeed.
* @return The zoomSpeed.
*/
public int getZoomSpeed() {
return zoomSpeed;
}
}
@@ -0,0 +1,78 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
import java.awt.*;
/**
* A context implementation used for the interface child repositioning packet.
* @author Emperor
*/
public final class ChildPositionContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* The interface id.
*/
private final int interfaceId;
/**
* The child id.
*/
private final int childId;
/**
* The new position of the child.
*/
private final Point position;
/**
* Constructs a new {@code ChildPositionContext} {@code Object}.
* @param player The player.
* @param interfaceId The interface id.
* @param childId The id of the child to reposition.
* @param positionX The new x-position.
* @param positionY The new y-position.
*/
public ChildPositionContext(Player player, int interfaceId, int childId, int positionX, int positionY) {
this.player = player;
this.interfaceId = interfaceId;
this.childId = childId;
this.position = new Point(positionX, positionY);
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the interfaceId.
* @return The interfaceId.
*/
public int getInterfaceId() {
return interfaceId;
}
/**
* Gets the childId.
* @return The childId.
*/
public int getChildId() {
return childId;
}
/**
* Gets the position.
* @return The position.
*/
public Point getPosition() {
return position;
}
}
@@ -0,0 +1,60 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.game.system.communication.ClanRepository;
import core.net.packet.Context;
/**
* The packet context for clan-related outgoing packets.
* @author Emperor
*/
public final class ClanContext implements Context {
/**
* The player
*/
private final Player player;
/**
* The clan instance.
*/
private final ClanRepository clan;
/**
* If the player is leaving the clan.
*/
private final boolean leave;
/**
* Constructs a new {@code ClanContext} {@code Object}.
* @param player the player.
* @param clan the clan.
* @param leave If the player is leaving the clan.
*/
public ClanContext(Player player, ClanRepository clan, boolean leave) {
this.player = player;
this.clan = clan;
this.leave = leave;
}
/**
* Gets the clan.
* @return The clan.
*/
public ClanRepository getClan() {
return clan;
}
/**
* Gets the leave.
* @return The leave.
*/
public boolean isLeave() {
return leave;
}
@Override
public Player getPlayer() {
return player;
}
}
@@ -0,0 +1,46 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.game.world.map.RegionChunk;
import core.net.packet.Context;
/**
* The packet context for the clear region chunk outgoing packet.
* @author Emperor
*/
public final class ClearChunkContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* The region chunk to clear.
*/
private final RegionChunk chunk;
/**
* Constructs a new {@code ClearChunkContext} {@code Object}.
* @param player The player.
* @param chunk The chunk to clear.
*/
public ClearChunkContext(Player player, RegionChunk chunk) {
this.player = player;
this.chunk = chunk;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the chunk.
* @return The chunk.
*/
public RegionChunk getChunk() {
return chunk;
}
}
@@ -0,0 +1,93 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The config packet context.
* @author Emperor
*/
public class ConfigContext implements Context {
/**
* The player reference.
*/
private Player player;
/**
* The config id.
*/
private int id;
/**
* The config value.
*/
private int value;
/**
* If the config is a cs2-config.
*/
private boolean cs2;
/**
* Construct a new {@code ConfigContext} {@code Object}.
* @param player The player reference.
* @param id The config id.
* @param value The config value.
*/
public ConfigContext(Player player, int id, int value) {
this(player, id, value, false);
}
/**
* Construct a new {@code ConfigContext} {@code Object}.
* @param player The player reference.
* @param id The config id.
* @param value The config value.
*/
public ConfigContext(Player player, int id, int value, boolean cs2) {
this.player = player;
this.id = id;
this.value = value;
this.cs2 = cs2;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Sets the player.
* @param player The player.
* @return This context instance.
*/
public Context setPlayer(Player player) {
this.player = player;
return this;
}
/**
* Get the config id.
* @return The config id.
*/
public int getId() {
return id;
}
/**
* Get the config value.
* @return The config value.
*/
public int getValue() {
return value;
}
/**
* Gets the cs2.
* @return The cs2.
*/
public boolean isCs2() {
return cs2;
}
}
@@ -0,0 +1,131 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The packet context for the contact packets.
* @author Emperor
*/
public final class ContactContext implements Context {
/**
* The update communication server state type.
*/
public static final int UPDATE_STATE_TYPE = 0;
/**
* The update friend type.
*/
public static final int UPDATE_FRIEND_TYPE = 1;
/**
* The ignore list type.
*/
public static final int IGNORE_LIST_TYPE = 2;
/**
* The player.
*/
private final Player player;
/**
* The contact packet type.
*/
private int type;
/**
* The player name.
*/
private String name;
/**
* The world id
*/
private int worldId;
/**
* Constructs a new {@code ContactContext} {@code Object}.
* @param player The player.
* @param type The contact packet type.
*/
public ContactContext(Player player, int type) {
this.player = player;
this.type = type;
}
/**
* Constructs a new {@code ContactContext} {@code Object}.
* @param player The player.
* @param name The player name.
* @param worldId The world id (0 = offline).
*/
public ContactContext(Player player, String name, int worldId) {
this.player = player;
this.name = name;
this.worldId = worldId;
this.type = UPDATE_FRIEND_TYPE;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the type.
* @return The type.
*/
public int getType() {
return type;
}
/**
* Sets the type.
* @param type The type to set.
*/
public void setType(int type) {
this.type = type;
}
/**
* Gets the name.
* @return The name.
*/
public String getName() {
return name;
}
/**
* Sets the name.
* @param name The name to set.
*/
public void setName(String name) {
this.name = name;
}
/**
* Gets the online.
* @return The online.
*/
public boolean isOnline() {
return worldId > 0;
}
/**
* Gets the worldId.
* @return the worldId
*/
public int getWorldId() {
return worldId;
}
/**
* Sets the worldId.
* @param worldId the worldId to set.
*/
public void setWorldId(int worldId) {
this.worldId = worldId;
}
}
@@ -0,0 +1,216 @@
package core.net.packet.context;
import core.game.container.Container;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.net.packet.Context;
/**
* Represents the context for the container packet.
* @author Emperor
*/
public final class ContainerContext implements Context {
/**
* The player.
*/
private Player player;
/**
* The interface id.
*/
private final int interfaceId;
/**
* The child id.
*/
private final int childId;
/**
* The container type.
*/
private final int containerId;
/**
* The items.
*/
private final Item[] items;
/**
* The length of the array to send.
*/
private final int length;
/**
* If the container should be split up.
*/
private final boolean split;
/**
* The slots we're changing.
*/
private final int[] slots;
/**
* If the container should be cleared.
*/
private boolean clear;
/**
* Constructs a new {@code ContainerContext} {@code Object}.
* @param player The player.
* @param interfaceId The interface id.
* @param childId The child id.
* @param clear If the container should be cleared.
*/
public ContainerContext(Player player, int interfaceId, int childId, boolean clear) {
this(player, interfaceId, childId, 0, null, 1, false);
this.clear = clear;
}
/**
* Constructs a new {@code ContainerContext} {@code Object}.
* @param player The player.
* @param interfaceId The interface id.
* @param childId The child id.
* @param containerId The container type.
* @param container The container.
* @param split If the container should be split.
*/
public ContainerContext(Player player, int interfaceId, int childId, int containerId, Container container, boolean split) {
this(player, interfaceId, childId, containerId, container.toArray(), container.toArray().length, split);
}
/**
* Constructs a new {@code ContainerContext} {@code Object}.
* @param player The player.
* @param interfaceId The interface id.
* @param childId The child id.
* @param containerId The container type.
* @param items The items.
* @param split If the container should be split.
*/
public ContainerContext(Player player, int interfaceId, int childId, int containerId, Item[] items, boolean split) {
this(player, interfaceId, childId, containerId, items, items.length, split);
}
/**
* Constructs a new {@code ContainerContext} {@code Object}.
* @param player The player.
* @param interfaceId The interface id.
* @param childId The child id.
* @param containerId The container containerId.
* @param items The items.
* @param length The length.
* @param split If the container should be split.
*/
public ContainerContext(Player player, int interfaceId, int childId, int containerId, Item[] items, int length, boolean split) {
this.player = player;
this.interfaceId = interfaceId;
this.childId = childId;
this.containerId = containerId;
this.items = items;
this.length = length;
this.split = split;
this.slots = null;
}
/**
* Constructs a new {@code ContainerContext} {@code Object}.
* @param player The player.
* @param interfaceId The interface id.
* @param childId The child id.
* @param containerId The container containerId.
* @param items The items.
* @param split If the container should be split.
* @param slots The slots to update.
*/
public ContainerContext(Player player, int interfaceId, int childId, int containerId, Item[] items, boolean split, int... slots) {
this.player = player;
this.interfaceId = interfaceId;
this.childId = childId;
this.containerId = containerId;
this.items = items;
this.length = items.length;
this.split = split;
this.slots = slots;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the interfaceId.
* @return The interfaceId.
*/
public int getInterfaceId() {
return interfaceId;
}
/**
* Gets the type.
* @return The type.
*/
public int getContainerId() {
return containerId;
}
/**
* Gets the items.
* @return The items.
*/
public Item[] getItems() {
return items;
}
/**
* Gets the length.
* @return The length.
*/
public int getLength() {
return length;
}
/**
* Gets the split.
* @return The split.
*/
public boolean isSplit() {
return split;
}
/**
* Gets the slots.
* @return The slots.
*/
public int[] getSlots() {
return slots;
}
/**
* Gets the childId.
* @return The childId.
*/
public int getChildId() {
return childId;
}
/**
* Gets the clear.
* @return The clear.
*/
public boolean isClear() {
return clear;
}
/**
* Sets the clear.
* @param clear The clear to set.
*/
public void setClear(boolean clear) {
this.clear = clear;
}
}
@@ -0,0 +1,56 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* Represents a default context of a packet.
* @author 'Vexia
*/
public class DefaultContext implements Context {
/**
* Represents the {@link Player} instance.
*/
private Player player;
/**
* Represents the array of objects casted.
*/
private Object[] objects;
/**
* Constructs a new {@code DefaultContext.java} {@code Object}.
* @param player the player.
* @param objects the objects.
*/
public DefaultContext(Player player, Object... objects) {
this.player = player;
this.objects = objects;
}
@Override
public Player getPlayer() {
return player;
}
/**
* @return the objects.
*/
public Object[] getObjects() {
return objects;
}
/**
* @param player the player to set.
*/
public void setPlayer(Player player) {
this.player = player;
}
/**
* @param objects the objects to set.
*/
public void setObjects(Object[] objects) {
this.objects = objects;
}
}
@@ -0,0 +1,174 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* Represents the player on interface context.
* @author Emperor
*/
public class DisplayModelContext implements Context {
/**
* Represents the model types.
* @author Emperor
*/
public static enum ModelType {
PLAYER, NPC, ITEM, MODEL;
}
/**
* The player reference.
*/
private final Player player;
/**
* The model type.
*/
private final ModelType type;
/**
* The node id.
*/
private final int nodeId;
/**
* The amount (for item display).
*/
private int amount;
/**
* The zoom.
*/
private int zoom;
/**
* The interface id.
*/
private final int interfaceId;
/**
* The child id.
*/
private final int childId;
/**
* Construct a new {@code DisplayModelContext} {@code Object} used for
* displaying the player.
* @param player The player reference.
* @param interfaceId The interface id.
* @param childId The child id.
*/
public DisplayModelContext(Player player, int interfaceId, int childId) {
this(player, ModelType.PLAYER, -1, 0, interfaceId, childId);
}
/**
* Construct a new {@code DisplayModelContext} {@code Object} used for
* displaying an NPC.
* @param player The player reference.
* @param nodeId The node id to display.
* @param interfaceId The interface id.
* @param childId The child id.
*/
public DisplayModelContext(Player player, int nodeId, int interfaceId, int childId) {
this(player, ModelType.NPC, nodeId, 0, interfaceId, childId);
}
/**
* Construct a new {@code DisplayModelContext} {@code Object} used by the
* other constructors or for displaying an item.
* @param player The player reference.
* @param type The model type.
* @param nodeId The node id to display.
* @param amount The amount.
* @param interfaceId The interface id.
* @param childId The child id.
*/
public DisplayModelContext(Player player, ModelType type, int nodeId, int amount, int interfaceId, int childId) {
this.player = player;
this.type = type;
this.nodeId = nodeId;
this.amount = amount;
this.interfaceId = interfaceId;
this.childId = childId;
}
/**
* Construct a new {@code DisplayModelContext} {@code Object} used by the
* other constructors or for displaying an item.
* @param player The player reference.
* @param type The model type.
* @param nodeId The node id to display.
* @param interfaceId The interface id.
* @param childId The child id.
*/
public DisplayModelContext(Player player, ModelType type, int nodeId, int zoom, int interfaceId, int childId, Object... object) {
this.player = player;
this.type = type;
this.nodeId = nodeId;
this.setZoom(zoom);
this.interfaceId = interfaceId;
this.childId = childId;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the type.
* @return The type.
*/
public ModelType getType() {
return type;
}
/**
* Gets the nodeId.
* @return The nodeId.
*/
public int getNodeId() {
return nodeId;
}
/**
* Gets the amount.
* @return The amount.
*/
public int getAmount() {
return amount;
}
/**
* Get the interface id.
* @return The interface id.
*/
public int getInterfaceId() {
return interfaceId;
}
/**
* Get the child id.
* @return The child id.
*/
public int getChildId() {
return childId;
}
/**
* @return the zoom.
*/
public int getZoom() {
return zoom;
}
/**
* @param zoom the zoom to set.
*/
public void setZoom(int zoom) {
this.zoom = zoom;
}
}
@@ -0,0 +1,20 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
/**
* Represents the packet context for the build dynamic scene graph packet.
* @author Emperor
*/
public final class DynamicSceneContext extends SceneGraphContext {
/**
* Constructs a new {@code DynamicSceneContext} {@code Object}.
* @param player The player.
* @param login If the player is logging in.
*/
public DynamicSceneContext(Player player, boolean login) {
super(player, login);
}
}
@@ -0,0 +1,44 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The game message packet context.
* @author Emperor
*/
public final class GameMessageContext implements Context {
/**
* The player reference.
*/
private Player player;
/**
* The game message.
*/
private String message;
/**
* Construct a new {@code GameMessageContext} {@code Object}.
* @param player The player.
* @param message The game message.
*/
public GameMessageContext(Player player, String message) {
this.player = player;
this.message = message;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Get the game message.
* @return The game message.
*/
public String getMessage() {
return message;
}
}
@@ -0,0 +1,52 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The packet context of the grand exchange update packet.
* @author Emperor
*/
public class GrandExchangeContext implements Context {
/**
* The player.
*/
private final Player player;
public final byte idx;
public final byte state;
public final short itemID;
public final boolean isSell;
public final int value;
public final int amt;
public final int completedAmt;
public final int totalCoinsExchanged;
/**
* Constructs a new {@code GrandExchangeContext} {@code Object}.
* @param player The player.
* @param state
* @param itemID
* @param value
* @param amt
* @param completedAmt
* @param totalCoinsExchanged
*/
public GrandExchangeContext(Player player, byte idx, byte state, short itemID, boolean isSell, int value, int amt, int completedAmt, int totalCoinsExchanged) {
this.player = player;
this.idx = idx;
this.state = state;
this.itemID = itemID;
this.isSell = isSell;
this.value = value;
this.amt = amt;
this.completedAmt = completedAmt;
this.totalCoinsExchanged = totalCoinsExchanged;
}
@Override
public Player getPlayer() {
return player;
}
}
@@ -0,0 +1,198 @@
package core.net.packet.context;
import core.game.node.Node;
import core.game.node.entity.Entity;
import core.game.node.entity.player.Player;
import core.game.world.map.Location;
import core.net.packet.Context;
/**
* Represents the hint icon packet context.
* @author Emperor
*/
public final class HintIconContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* The hint icon slot.
*/
private final int slot;
/**
* The target type.
*/
private int targetType;
/**
* The arrow id.
*/
private int arrowId;
/**
* The entity index.
*/
private final int index;
/**
* The model id.
*/
private final int modelId;
/**
* The location.
*/
private final Location location;
/**
* The height.
*/
private int height;
/**
* Constructs a new {@code HintIconContext} {@code Object}. <br> This hint
* icon is entity based.
* @param player The player.
* @param slot The hint icon slot.
* @param arrowId The arrow id.
* @param target The target.
* @param modelId The model id.
*/
public HintIconContext(Player player, int slot, int arrowId, Node target, int modelId) {
this(player, slot, arrowId, -1, target, modelId);
targetType = 2;
if (target instanceof Entity) {
targetType = target instanceof Player ? 10 : 1;
}
}
/**
* Constructs a new {@code HintIconContext} {@code Object}. <br> This hint
* icon is entity based.
* @param player The player.
* @param slot The hint icon slot.
* @param arrowId The arrow id.
* @param targetType The target type.
* @param target The index of the entity.
* @param modelId The model id.
*/
public HintIconContext(Player player, int slot, int arrowId, int targetType, Node target, int modelId) {
this(player, slot, arrowId, targetType, target, modelId, 0);
}
/**
* Constructs a new {@code HintIconContext} {@code Object}. <br> This hint
* icon is entity based.
* @param player The player.
* @param slot The hint icon slot.
* @param arrowId The arrow id.
* @param targetType The target type.
* @param target The index of the entity.
* @param modelId The model id.
* @param height The height.
*/
public HintIconContext(Player player, int slot, int arrowId, int targetType, Node target, int modelId, int height) {
this.player = player;
this.slot = slot;
this.targetType = targetType;
this.arrowId = arrowId;
this.modelId = modelId;
this.height = height;
if (target instanceof Entity) {
this.index = ((Entity) target).getIndex();
this.location = null;
} else {
this.location = target.getLocation();
this.index = -1;
}
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the slot.
* @return The slot.
*/
public int getSlot() {
return slot;
}
/**
* Gets the targetType.
* @return The targetType.
*/
public int getTargetType() {
return targetType;
}
/**
* Gets the arrowId.
* @return The arrowId.
*/
public int getArrowId() {
return arrowId;
}
/**
* Gets the index.
* @return The index.
*/
public int getIndex() {
return index;
}
/**
* Gets the modelId.
* @return The modelId.
*/
public int getModelId() {
return modelId;
}
/**
* Gets the location.
* @return The location.
*/
public Location getLocation() {
return location;
}
/**
* Sets the targetType.
* @param targetType The targetType to set.
*/
public void setTargetType(int targetType) {
this.targetType = targetType;
}
/**
* Sets the arrowId.
* @param arrowId The arrowId to set.
*/
public void setArrowId(int arrowId) {
this.arrowId = arrowId;
}
/**
* Gets the height.
* @return The height.
*/
public int getHeight() {
return height;
}
/**
* Sets the height.
* @param height The height to set.
*/
public void setHeight(int height) {
this.height = height;
}
}
@@ -0,0 +1,51 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The <b>Integer</b> context.
* @author 'Vexia
*/
public class IntegerContext implements Context {
/**
* The player instance.
*/
private Player player;
/**
* The integer value.
*/
private int integer;
/**
* Constructs a new {@code IntegerContext.java} {@code Object}.
* @param player the player.
* @param integer the integer.
*/
public IntegerContext(Player player, int integer) {
this.player = player;
this.setInteger(integer);
}
@Override
public Player getPlayer() {
return player;
}
/**
* @return the integer.
*/
public int getInteger() {
return integer;
}
/**
* @param integer the integer to set.
*/
public void setInteger(int integer) {
this.integer = integer;
}
}
@@ -0,0 +1,74 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The context implementation used for the InteractionOption outgoing packet.
* @author Emperor
*/
public final class InteractionOptionContext implements Context {
/**
* The player reference.
*/
private final Player player;
/**
* The index.
*/
private final int index;
private boolean remove = false;
/**
* The name.
*/
private final String name;
/**
* Constructs a new {@code InteractionOptionContext} {@code Object}.
* @param player The player.
* @param index The index.
* @param name The option name.
*/
public InteractionOptionContext(Player player, int index, String name) {
this.player = player;
this.index = index;
this.name = name;
this.remove = false;
}
public InteractionOptionContext(Player player, int index, String name, boolean remove){
this.player = player;
this.index = index;
this.name = name;
this.remove = remove;
}
@Override
public Player getPlayer() {
return player;
}
public boolean isRemove() {
return remove;
}
/**
* Gets the index.
* @return The index.
*/
public int getIndex() {
return index;
}
/**
* Gets the name.
* @return The name.
*/
public String getName() {
return name;
}
}
@@ -0,0 +1,74 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The interface config packet context.
* @author Emperor
*/
public class InterfaceConfigContext implements Context {
/**
* The player reference.
*/
private Player player;
/**
* The interface id.
*/
private int interfaceId;
/**
* The child id.
*/
private int childId;
/**
* If the interface child should be hidden.
*/
private boolean hide;
/**
* Construct a new {@code InterfaceConfigContext} {@code Object}.
* @param player The player reference.
* @param interfaceId The interface id.
* @param childId The child id.
* @param hide If the component should be hidden.
*/
public InterfaceConfigContext(Player player, int interfaceId, int childId, boolean hide) {
this.player = player;
this.interfaceId = interfaceId;
this.childId = childId;
this.hide = hide;
}
/**
* Get the interface id.
* @return The interface id.
*/
public int getInterfaceId() {
return interfaceId;
}
/**
* Get the child id.
* @return The child id.
*/
public int getChildId() {
return childId;
}
/**
* If is set.
* @return If is set {@code true}.
*/
public boolean isHidden() {
return hide;
}
@Override
public Player getPlayer() {
return player;
}
}
@@ -0,0 +1,118 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The interface packet context.
* @author Emperor
*/
public final class InterfaceContext implements Context {
/**
* The player.
*/
private Player player;
/**
* The window id.
*/
private final int windowId;
/**
* The component id.
*/
private int componentId;
/**
* The interface id.
*/
private final int interfaceId;
/**
* If the interface can be walked over.
*/
private final boolean walkable;
/**
* Constructs a new {@code InterfaceContext} {@code Object}.
* @param player The player.
* @param windowId The window id.
* @param componentId The window component id.
* @param interfaceId The interface id.
* @param walkable If we can walk over the interface.
*/
public InterfaceContext(Player player, int windowId, int componentId, int interfaceId, boolean walkable) {
this.player = player;
this.windowId = windowId;
this.componentId = componentId;
this.interfaceId = interfaceId;
this.walkable = walkable;
}
/**
* Transforms this context for the new player & id.
* @param player The player.
* @param id The new interface id.
* @return The interface context created.
*/
public InterfaceContext transform(Player player, int id) {
return new InterfaceContext(player, windowId, componentId, id, walkable);
}
@Override
public Player getPlayer() {
return player;
}
/**
* Sets the player.
* @param player The player.
* @return This context instance.
*/
public Context setPlayer(Player player) {
this.player = player;
return this;
}
/**
* Gets the windowId.
* @return The windowId.
*/
public int getWindowId() {
return windowId;
}
/**
* Sets the component id.
* @param componentId The component id.
*/
public void setComponentId(int componentId) {
this.componentId = componentId;
}
/**
* Gets the componentId.
* @return The componentId.
*/
public int getComponentId() {
return componentId;
}
/**
* Gets the interfaceId.
* @return The interfaceId.
*/
public int getInterfaceId() {
return interfaceId;
}
/**
* Gets the walkable.
* @return The walkable.
*/
public boolean isWalkable() {
return walkable;
}
}
@@ -0,0 +1,61 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.game.world.map.Location;
import core.net.packet.Context;
/**
* Packet context used for location based packets.
* @author Emperor
*/
public final class LocationContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* The location.
*/
private final Location location;
/**
* If the location update is flagged as a teleport.
*/
private final boolean teleport;
/**
* Constructs a new {@code LocationContext} {@code Object}.
* @param player The player.
* @param location The location.
* @param teleport If the location update is flagged as a teleport.
*/
public LocationContext(Player player, Location location, boolean teleport) {
this.player = player;
this.location = location;
this.teleport = teleport;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the location.
* @return The location.
*/
public Location getLocation() {
return location;
}
/**
* Gets the teleport.
* @return The teleport.
*/
public boolean isTeleport() {
return teleport;
}
}
@@ -0,0 +1,119 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.game.node.entity.player.info.Rights;
import core.net.packet.Context;
/**
* Packet context for communication message packets.
* @author Emperor
*/
public final class MessageContext implements Context {
/**
* Represents the packet id to use when sending a message.
*/
public static final int SEND_MESSAGE = 71;
/**
* Represents the packet id to use when receiving a message.
*/
public static final int RECIEVE_MESSAGE = 0;
/**
* Represents the packet id use to send a clan message.
*/
public static final int CLAN_MESSAGE = 54;
/**
* The player.
*/
private final Player player;
/**
* The other player.
*/
private final String other;
/**
* The chat icon.
*/
private final int chatIcon;
/**
* The opcode.
*/
private final int opcode;
/**
* The message.
*/
private final String message;
/**
* Constructs a new MessageContext Object.
* @param player The player.
* @param other The communicated player.
* @param opcode The opcode.
* @param message The message.
*/
public MessageContext(Player player, Player other, int opcode, String message) {
this.player = player;
this.other = other.getName();
this.chatIcon = Rights.getChatIcon(other);
this.opcode = opcode;
this.message = message;
}
/**
* Constructs a new MessageContext Object.
* @param player The player.
* @param other The communicated player.
* @param opcode The opcode.
* @param message The message.
*/
public MessageContext(Player player, String other, int chatIcon, int opcode, String message) {
this.player = player;
this.other = other;
this.chatIcon = chatIcon;
this.opcode = opcode;
this.message = message;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the other.
* @return The other.
*/
public String getOther() {
return other;
}
/**
* Gets the opcode.
* @return The opcode.
*/
public int getOpcode() {
return opcode;
}
/**
* Gets the message.
* @return The message.
*/
public String getMessage() {
return message;
}
/**
* Gets the chatIcon.
* @return the chatIcon
*/
public int getChatIcon() {
return chatIcon;
}
}
@@ -0,0 +1,45 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* Represents the context used for the Minimap State packet.
* @author Emperor
*/
public class MinimapStateContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* The minimap state to set.
*/
private final int state;
/**
* Constructs a new {@code MinimapStateContext} {@code Object}.
* @param player The player.
* @param state The minimap state to set.
*/
public MinimapStateContext(Player player, int state) {
this.player = player;
this.state = state;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the state.
* @return The state.
*/
public int getState() {
return state;
}
}
@@ -0,0 +1,78 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* Packet context for music.
* @author Emperor
* @author SonicForce41
*/
public class MusicContext implements Context {
/**
* The Player
*/
private Player player;
/**
* The music Id
*/
private int musicId;
/**
* The secondary music type.
*/
private boolean secondary;
/**
* Constructs a new {@code MusicContext} {@code Object}.
* @param player The player.
* @param musicId The music id.
*/
public MusicContext(Player player, int musicId) {
this(player, musicId, false);
}
/**
* Constructs a new {@code MusicContext} {@code Object}.
* @param player The player.
* @param musicId The music id.
* @param temporary The temporary music type.
*/
public MusicContext(Player player, int musicId, boolean temporary) {
this.player = player;
this.musicId = musicId;
this.secondary = temporary;
}
/**
* Gets the Music Id
* @return the musicId
*/
public final int getMusicId() {
return musicId;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the secondary.
* @return The secondary.
*/
public boolean isSecondary() {
return secondary;
}
/**
* Sets the secondary.
* @param secondary The secondary to set.
*/
public void setSecondary(boolean secondary) {
this.secondary = secondary;
}
}
@@ -0,0 +1,30 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The default packet context.
* @author Emperor
*/
public final class PlayerContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* Constructs a new {@code PlayerContext} {@code Object}.
* @param player The player.
*/
public PlayerContext(Player player) {
this.player = player;
}
@Override
public Player getPlayer() {
return player;
}
}
@@ -0,0 +1,67 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.game.world.map.Location;
import core.game.world.update.flag.context.Graphics;
import core.net.packet.Context;
/**
* The packet context for the positioned graphic packet.
* @author Emperor
*/
public final class PositionedGraphicContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* The graphics.
*/
private final Graphics graphic;
/**
* The location.
*/
private final Location location;
public int sceneX, sceneY;
public int offsetX, offsetY;
/**
* Constructs a new {@code PositionedGraphicContext} {@code Object}.
* @param player The player.
* @param graphic The graphic to display on the given location.
* @param location The location to display the graphic on.
*/
public PositionedGraphicContext(Player player, Graphics graphic, Location location, int offsetX, int offsetY) {
this.player = player;
this.graphic = graphic;
this.location = location;
this.sceneX = location.getSceneX(player.getPlayerFlags().getLastSceneGraph());
this.sceneY = location.getSceneY(player.getPlayerFlags().getLastSceneGraph());
this.offsetX = offsetX;
this.offsetY = offsetY;
}
@Override
public Player getPlayer() {
return player;
}
/**
* @return the graphic
*/
public Graphics getGraphic() {
return graphic;
}
/**
* @return the location
*/
public Location getLocation() {
return location;
}
}
@@ -0,0 +1,84 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The run script packet context.
* @author Emperor
*/
public class RunScriptContext implements Context {
/**
* The player reference.
*/
private Player player;
/**
* The run script id.
*/
private int id;
/**
* The parameters.
*/
private Object[] objects;
/**
* The string.
*/
private String string;
/**
* Construct a new {@code RunScriptContext} {@code Object}.
* @param player
* @param id
* @param string
* @param objects
*/
public RunScriptContext(Player player, int id, String string, Object... objects) {
this.player = player;
this.id = id;
this.objects = objects;
this.string = string;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Sets the player.
* @param player The player.
* @return This context instance.
*/
public Context setPlayer(Player player) {
this.player = player;
return this;
}
/**
* Get the run scripts id.
* @return The run scripts id.
*/
public int getId() {
return id;
}
/**
* Get the objects.
* @return The objects.
*/
public Object[] getObjects() {
return objects;
}
/**
* Get the string.
* @return The string.
*/
public String getString() {
return string;
}
}
@@ -0,0 +1,45 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The update scene graph packet context.
* @author Emperor
*/
public class SceneGraphContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* If we are logging in.
*/
private final boolean login;
/**
* Constructs a new {@code SceneGraphContext} {@code Object}.
* @param player The player.
* @param login If we are logging in.
*/
public SceneGraphContext(Player player, boolean login) {
this.player = player;
this.login = login;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the login.
* @return The login.
*/
public boolean isLogin() {
return login;
}
}
@@ -0,0 +1,45 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The skill context.
* @author Emperor
*/
public final class SkillContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* The skill id.
*/
private final int skillId;
/**
* Constructs a new {@code SkillContext} {@code Object}.
* @param player The player.
* @param skillId The skill id.
*/
public SkillContext(Player player, int skillId) {
this.player = player;
this.skillId = skillId;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the skillId.
* @return The skillId.
*/
public int getSkillId() {
return skillId;
}
}
@@ -0,0 +1,74 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* The StringPacket packet context.
* @author Emperor
*/
public class StringContext implements Context {
/**
* The player reference.
*/
private Player player;
/**
* The StringPacket string.
*/
private String string;
/**
* The interface id.
*/
private int interfaceId;
/**
* The line id.
*/
private int lineId;
/**
* Constructs a new {@code StringContext} {@code Object}.
* @param player The player.
* @param string The string to send.
* @param interfaceId The interface id.
* @param lineId The child id.
*/
public StringContext(Player player, String string, int interfaceId, int lineId) {
this.player = player;
this.string = string;
this.interfaceId = interfaceId;
this.lineId = lineId;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Get the StringPacket string.
* @return The string.
*/
public String getString() {
return string;
}
/**
* Get the interface id.
* @return The interface id.
*/
public int getInterfaceId() {
return interfaceId;
}
/**
* Gets the line id.
* @return The line id.
*/
public int getLineId() {
return lineId;
}
}
@@ -0,0 +1,51 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* Represents a system update.
* @author 'Vexia
*/
public class SystemUpdateContext implements Context {
/**
* The <b>Player</b> instance.
*/
private Player player;
/**
* The time.
*/
private int time;
/**
* Constructs a new {@code SystemUpdateContext.java} {@code Object}.
* @param player the <b>Player</b>.
* @param time the time.
*/
public SystemUpdateContext(Player player, int time) {
this.player = player;
this.setTime(time);
}
@Override
public Player getPlayer() {
return player;
}
/**
* @return the time
*/
public int getTime() {
return time;
}
/**
* @param time the time to set
*/
public void setTime(int time) {
this.time = time;
}
}
@@ -0,0 +1,22 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
public class VarbitContext implements Context {
Player player;
public int varbitId;
public int value;
public VarbitContext(Player player, int varbitId, int value){
this.player = player;
this.varbitId = varbitId;
this.value = value;
}
@Override
public Player getPlayer() {
return player;
}
}
@@ -0,0 +1,23 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
public class VarcUpdateContext implements Context {
Player player;
public int varcId;
public int value;
public VarcUpdateContext(Player player, int varcId, int value){
this.player = player;
this.varcId = varcId;
this.value = value;
}
@Override
public Player getPlayer() {
return player;
}
}
@@ -0,0 +1,45 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* Represents the set walk-to option context.
* @author Emperor
*/
public final class WalkOptionContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* The option.
*/
private final String option;
/**
* Constructs a new {@code WalkOptionContext} {@code Object}.
* @param player The player.
* @param option The option name.
*/
public WalkOptionContext(Player player, String option) {
this.player = player;
this.option = option;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the option.
* @return The option.
*/
public String getOption() {
return option;
}
}
@@ -0,0 +1,60 @@
package core.net.packet.context;
import core.game.node.entity.player.Player;
import core.net.packet.Context;
/**
* Represents the windows pane packet context.
* @author Emperor
*/
public final class WindowsPaneContext implements Context {
/**
* The player.
*/
private final Player player;
/**
* The window id.
*/
private final int windowId;
/**
* The type.
*/
private final int type;
/**
* Constructs a new {@code WindowsPaneContext} object.
* @param player The player.
* @param windowId The window id.
* @param type The type.
*/
public WindowsPaneContext(Player player, int windowId, int type) {
this.player = player;
this.windowId = windowId;
this.type = type;
}
@Override
public Player getPlayer() {
return player;
}
/**
* Gets the windowId.
* @return The windowId.
*/
public int getWindowId() {
return windowId;
}
/**
* Gets the type.
* @return The type.
*/
public int getType() {
return type;
}
}
@@ -0,0 +1,792 @@
package core.net.packet.`in`
import core.game.node.entity.player.Player
import core.net.packet.IoBuffer
import core.tools.StringUtils
import core.tools.SystemLogger
import java.io.PrintWriter
import java.io.StringWriter
enum class Decoders530(val opcode: Int) {
/******************************************
* ITEM INTERACTIONS
******************************************/
ITEM_ACTION_1(156) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val slot = buffer.leShortA
val itemId = buffer.shortA
val ifHash = buffer.leInt
val (iface, button) = deHash(ifHash)
return Packet.ItemAction(player, 0, itemId, slot, iface, button)
}
},
ITEM_ACTION_2(55) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val itemId = buffer.leShort
val slot = buffer.shortA
val ifHash = buffer.intA
val (iface, button) = deHash(ifHash)
return Packet.ItemAction(player, 1, itemId, slot, iface, button)
}
},
ITEM_ACTION_3(153) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.leInt
val slot = buffer.leShort
val itemId = buffer.leShort
val (iface, button) = deHash(ifHash)
return Packet.ItemAction(player, 2, itemId, slot, iface, button)
}
},
ITEM_ACTION_4(161) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.leInt
val itemId = buffer.leShortA
val slot = buffer.leShortA
val (iface, button) = deHash(ifHash)
return Packet.ItemAction(player, 3, itemId, slot, iface, button)
}
},
ITEM_ACTION_5(135) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val itemId = buffer.shortA
val slot = buffer.shortA
val ifHash = buffer.intB
val (iface, button) = deHash(ifHash)
return Packet.ItemAction(player, 4, itemId, slot, iface, button)
}
},
/******************************************
* NPC INTERACTIONS
******************************************/
NPC_ACTION_1(78) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val npcIndex = buffer.leShort
return Packet.NpcAction(player, 0, npcIndex)
}
},
NPC_ACTION_2(3) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val npcIndex = buffer.leShortA
return Packet.NpcAction(player, 1, npcIndex)
}
},
NPC_ACTION_3(148) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val npcIndex = buffer.shortA
return Packet.NpcAction(player, 2, npcIndex)
}
},
NPC_ACTION_4(30) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val npcIndex = buffer.short
return Packet.NpcAction(player, 3, npcIndex)
}
},
NPC_ACTION_5(218) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val npcIndex = buffer.leShort
return Packet.NpcAction(player, 4, npcIndex)
}
},
/******************************************
* SCENERY INTERACTIONS
******************************************/
SCENERY_ACTION_1(254) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val x = buffer.leShort
val objId = buffer.shortA and 0xFFFF
val y = buffer.short
return Packet.SceneryAction(player, 0, objId, x, y)
}
},
SCENERY_ACTION_2(194) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val y = buffer.leShortA
val x = buffer.leShort
val objId = buffer.short and 0xFFFF
return Packet.SceneryAction(player, 1, objId, x, y)
}
},
SCENERY_ACTION_3(84) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val objId = buffer.leShortA and 0xFFFF
val y = buffer.leShortA
val x = buffer.leShort
return Packet.SceneryAction(player, 2, objId, x, y)
}
},
SCENERY_ACTION_4(247) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val y = buffer.leShort and 0xFFFF
val x = buffer.leShortA
val objId = buffer.short and 0xFFFF
return Packet.SceneryAction(player, 3, objId, x, y)
}
},
SCENERY_ACTION_5(170) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val objId = buffer.leShortA and 0xFFFF
val x = buffer.leShortA
val y = buffer.leShortA
return Packet.SceneryAction(player, 4, objId, x, y)
}
},
/******************************************
* PLAYER INTERACTIONS
******************************************/
PLAYER_ACTION_1(68) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val pIndex = buffer.leShortA
return Packet.PlayerAction(player, 0, pIndex)
}
},
PLAYER_ACTION_3(71) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val pIndex = buffer.leShortA
return Packet.PlayerAction(player, 2, pIndex)
}
},
PLAYER_ACTION_4(180) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val pIndex = buffer.leShortA
return Packet.PlayerAction(player, 3, pIndex)
}
},
PLAYER_ACTION_7(114) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val index = buffer.leShortA
return Packet.PlayerAction(player, 6, index)
}
},
PLAYER_ACTION_8(175) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val pIndex = buffer.shortA
return Packet.PlayerAction(player, 7, pIndex)
}
},
/******************************************
* OBJSTACK (Ground Item) INTERACTIONS
******************************************/
OBJSTACK_ACTION_1(66) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val x = buffer.leShort
val itemId = buffer.short
val y = buffer.leShortA
return Packet.GroundItemAction(player, 2, itemId, x, y)
}
},
OBJSTACK_ACTION_2(33) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val itemId = buffer.short
val x = buffer.short
val y = buffer.leShort
return Packet.GroundItemAction(player, 3, itemId, x, y)
}
},
/******************************************
* USEWITH INTERACTIONS
******************************************/
USEWITH_NPC(115) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.intB
val slot = buffer.leShort
val npcIndex = buffer.leShort
val itemId = buffer.leShortA
val (iface, _) = deHash(ifHash)
return Packet.UseWithNpc(player, itemId, npcIndex, iface, slot)
}
},
USEWIH_PLAYER(248) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val otherIndex = buffer.leShortA
val itemId = buffer.short
val slot = buffer.short
val ifHash = buffer.intB
val (iface, _) = deHash(ifHash)
return Packet.UseWithPlayer(player, itemId, otherIndex, iface, slot)
}
},
USEWITH_ITEM(27) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val usedSlot = buffer.short
val usedIfHash = buffer.leInt
val usedWithSlot = buffer.leShort
val usedWithIfHash = buffer.leInt
val usedId = buffer.leShortA
val usedWithId = buffer.leShortA
val (usedIface, usedChild) = deHash(usedIfHash)
val (usedWithIface, usedWithChild) = deHash(usedWithIfHash)
return Packet.UseWithItem(
player,
usedId, usedWithId,
usedSlot, usedWithSlot,
usedIface, usedWithIface,
usedChild, usedWithChild
)
}
},
USEWITH_SCENERY(134) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val x = buffer.shortA
val id = buffer.short
val y = buffer.leShort
val slot = buffer.short
buffer.leShort //Suspicious noops? TODO: FIND OUT WHAT THESE ARE
buffer.short
val sceneryId = buffer.shortA
return Packet.UseWithScenery(player, id, slot, sceneryId, x, y)
}
},
USEWITH_GROUNDITEM(101) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val x = buffer.leShortA
val slot = buffer.leShort
val usedId = buffer.leShort
val usedWithId = buffer.leShort
val y = buffer.leShortA
val ifHash = buffer.intB
val (iface,child) = deHash(ifHash)
return Packet.UseWithGroundItem(player, usedId, usedWithId, iface, child, slot, x, y)
}
},
/******************************************
* INTERFACE INTERACTIONS
******************************************/
IF_ACTION_1(155) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.int
val slot = buffer.short
val (iface, button) = deHash(ifHash)
return Packet.IfAction(player, opcode, 0, iface, button, slot)
}
},
IF_ACTION_2(196) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.int
val slot = buffer.short
val (iface, button) = deHash(ifHash)
return Packet.IfAction(player, opcode, 1, iface, button, slot)
}
},
IF_ACTION_3(124) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.int
val slot = buffer.short
val (iface, button) = deHash(ifHash)
return Packet.IfAction(player, opcode, 2, iface, button, slot)
}
},
IF_ACTION_4(199) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.int
val slot = buffer.short
val (iface, button) = deHash(ifHash)
return Packet.IfAction(player, opcode, 3, iface, button, slot)
}
},
IF_ACTION_5(234) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.int
val slot = buffer.short
val (iface, button) = deHash(ifHash)
return Packet.IfAction(player, opcode, 4, iface, button, slot)
}
},
IF_ACTION_6(168) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.int
val slot = buffer.short
val (iface, button) = deHash(ifHash)
return Packet.IfAction(player, opcode, 5, iface, button, slot)
}
},
IF_ACTION_7(166) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.int
val slot = buffer.short
val (iface, button) = deHash(ifHash)
return Packet.IfAction(player, opcode, 6, iface, button, slot)
}
},
IF_ACTION_8(64) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.int
val slot = buffer.short
val (iface, button) = deHash(ifHash)
return Packet.IfAction(player, opcode, 7, iface, button, slot)
}
},
IF_ACTION_9(53) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.int
val slot = buffer.short
val (iface, button) = deHash(ifHash)
return Packet.IfAction(player, opcode, 8, iface, button, slot)
}
},
IF_ACTION_10(9) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.int
val slot = buffer.short
val (iface, button) = deHash(ifHash)
return Packet.IfAction(player, opcode, 9, iface, button, slot)
}
},
IF_ACTION_CS(10) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.int
val (iface, button) = deHash(ifHash)
return Packet.IfAction(player, opcode, -1, iface, button, -1, -1)
}
},
CONTINUE_OPT(132) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.intA
val slot = buffer.leShort
val (iface, button) = deHash(ifHash)
return Packet.ContinueOption(player, iface, button, slot, 132)
}
},
CLOSE_IFACE(184) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.CloseIface(player)
}
},
IF_GROUNDITEM_ACTION(73) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val ifHash = buffer.intA
val (iface, child) = deHash(ifHash)
val y = buffer.short
val itemId = buffer.leShortA
val x = buffer.leShortA
val slot = buffer.leShort
return Packet.ComponentGroundItemAction(player, iface, child, slot, itemId, x, y)
}
},
IF_PLAYER_ACTION(195) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
buffer.shortA //Arios ignoring more data... nice
val child = buffer.leShort
val iface = buffer.leShort
val otherIndex = buffer.leShortA
return Packet.ComponentPlayerAction(player, otherIndex, iface, child)
}
},
IF_SCENERY_ACTION(233) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val y = buffer.leShortA
val x = buffer.shortA
val itemId = buffer.leShortA //probably misnamed/mislabeled by Arios.
val ifHash = buffer.intA
val (iface, child) = deHash(ifHash)
val objId = buffer.shortA
return Packet.ComponentSceneryAction(player, iface, child, objId, x, y)
}
},
IF_NPC_ACTION(239) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val child = buffer.leShort
val iface = buffer.leShort
buffer.shortA //more ignored data....
val index = buffer.leShortA
return Packet.ComponentNpcAction(player, iface, child, index)
}
},
IF_ITEM_ACTION(253) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val child = buffer.leShort
val iface = buffer.leShort
val itemSlot = buffer.leShortA
val unused = buffer.leInt //unused???
val itemId = buffer.shortA
buffer.shortA //more ignored data....
return Packet.ComponentItemAction(player, iface, child, itemId, itemSlot)
}
},
SLOTSWITCH_MULTI(79) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val srcHash = buffer.intB
val destSlot = buffer.leShort
val destHash = buffer.int
val srcSlot = buffer.leShort
val (srcIface, srcChild) = deHash(srcHash)
val (destIface, destChild) = deHash(destHash)
return Packet.SlotSwitchMultiComponent(player, srcIface, srcChild, srcSlot, destIface, destChild, destSlot)
}
},
SLOTSWITCH_SINGLE(231) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val srcSlot = buffer.short
val ifHash = buffer.leInt
val destSlot = buffer.shortA
val isInsert = buffer.get() == 1
val (iface, child) = deHash(ifHash)
return Packet.SlotSwitchSingleComponent(player, iface, child, srcSlot, destSlot, isInsert)
}
},
IF_ITEM_OPT_1(81) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val slot = buffer.shortA
val itemId = buffer.short
val child = buffer.short
val iface = buffer.short
return Packet.IfAction(player, opcode, 0, iface, child, slot, itemId)
}
},
IF_ITEM_OPT_2(206) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val itemId = buffer.shortA
val slot = buffer.leShort
val ifHash = buffer.leInt
val (iface, button) = deHash(ifHash)
return Packet.IfAction(player, opcode, 1, iface, button, slot, itemId)
}
},
/******************************************
* EXAMINE
******************************************/
EXAMINE_SCENERY(94) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.SceneryExamine(player, buffer.leShortA)
}
},
EXAMINE_ITEM(92) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.ItemExamine(player, buffer.leShortA)
}
},
EXAMINE_NPC(72) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.NpcExamine(player, buffer.short)
}
},
/******************************************
* CLANS
******************************************/
CLAN_JOIN(104) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val clanName = StringUtils.longToString(buffer.long)
return Packet.JoinClan(player, clanName)
}
},
CLAN_SETRANK(188) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val rank = buffer.a
val name = StringUtils.longToString(buffer.long)
return Packet.SetClanRank(player, name, rank)
}
},
CLAN_KICK(162) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val name = StringUtils.longToString(buffer.long)
return Packet.KickFromClan(player, name)
}
},
/******************************************
* FRIENDS/IGNORES
******************************************/
ADD_FRIEND(120) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.AddFriend(player, StringUtils.longToString(buffer.long))
}
},
REMOVE_FRIEND(57) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.RemoveFriend(player, StringUtils.longToString(buffer.long))
}
},
ADD_IGNORE(34) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.AddIgnore(player, StringUtils.longToString(buffer.long))
}
},
REMOVE_IGNORE(213) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.RemoveIgnore(player, StringUtils.longToString(buffer.long))
}
},
PRIVATE_MESSAGE(201) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val name = StringUtils.longToString(buffer.long)
val message = StringUtils.decryptPlayerChat(buffer, buffer.get() and 0xFF)
return Packet.PrivateMessage(player, name, message)
}
},
/******************************************
* INPUT TRACKING
******************************************/
FOCUS_CHANGE(22) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.TrackingFocus(player, buffer.get() == 1)
}
},
CAMERA_MOVEMENT(21) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val x = buffer.shortA
val y = buffer.leShort
return Packet.TrackingCameraPos(player, x, y)
}
},
DISPLAY_UPDATE(243) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val windowMode = buffer.get()
val screenWidth = buffer.short
val screenHeight = buffer.short
val displayMode = buffer.get()
return Packet.TrackingDisplayUpdate(player, windowMode, screenWidth, screenHeight, displayMode)
}
},
AFK_TIMEOUT(245) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.TrackingAfkTimeout(player)
}
},
MOUSE_CLICKED(75) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val data = buffer.leShortA
val pos = buffer.intB
val rightClick = ((data shr 15) and 0x1) == 1
val delay = data and 0x7FF
val x = pos shr 16
val y = pos and 0xFFFF
return Packet.TrackingMouseClick(player, x, y, rightClick, delay)
}
},
/******************************************
* WALKING
******************************************/
WORLDSPACE_WALK(215) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val (running, x, y) = decodeWalkInformation(buffer, false)
return Packet.WorldspaceWalk(player, x, y, running)
}
},
MINIMAP_WALK(39) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val (running, x, y) = decodeWalkInformation(buffer, true)
val clickedX = buffer.get()
val clickedY = buffer.get()
val rotation = buffer.short
//Unlabeled data ignored by arios
buffer.get()
buffer.get()
buffer.get()
buffer.get()
buffer.short
buffer.short
buffer.get()
buffer.get()
return Packet.MinimapWalk(player, x, y, clickedX, clickedY, rotation, running)
}
},
INTERACT_WALK(77) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val (running, x, y) = decodeWalkInformation(buffer, false)
return Packet.InteractWalk(player, x, y, running)
}
},
/******************************************
* INPUT PROMPT RESPONSE
******************************************/
INPUT_SHORT_STRING_RESPONSE(244) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.InputPromptResponse(player, StringUtils.longToString(buffer.long))
}
},
INPUT_LONG_STRING_RESPONSE(65) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.InputPromptResponse(player, buffer.string)
}
},
INPUT_INT_RESPONSE(23) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.InputPromptResponse(player, buffer.int)
}
},
/******************************************
* ASSORTED
******************************************/
GE_SET_OFFER_ITEM(111) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val itemId = buffer.short
return Packet.GESetOfferItem(player, itemId)
}
},
COMMAND(44) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
if (buffer.toByteBuffer().remaining() > 1) {
val message = buffer.string.lowercase()
return Packet.Command(player, message)
}
return Packet.NoProcess()
}
},
CHAT_SETTINGS(157) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.ChatSetting(
player,
buffer.get(),
buffer.get(),
buffer.get()
)
}
},
CHAT_MESSAGE(237) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val effects = buffer.short
val numChars = buffer.smart
val message = StringUtils.decryptPlayerChat(buffer, numChars)
return Packet.ChatMessage(player, effects, message)
}
},
MUSIC_TRACK_FINISHED(137) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val trackId = buffer.leShortA
return Packet.TrackFinished(player, trackId)
}
},
REPORT_ABUSE(99) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val target = StringUtils.longToString(buffer.long)
val ruleId = buffer.get()
val modMute = buffer.get() == 1
return Packet.ReportAbuse(player, target, ruleId, modMute)
}
},
UPDATE_PACKET_COUNT(177) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val count = buffer.short
return Packet.PacketCountUpdate(player, count)
}
},
PING(93) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.Ping(player)
}
},
QUICKCHAT(167) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val x = buffer.toByteBuffer()
val packetType = when(x.array().size){
3,4 -> QCPacketType.STANDARD
5 -> QCPacketType.SINGLE
7 -> QCPacketType.DOUBLE
else -> QCPacketType.UNHANDLED.also { SystemLogger.logWarn(this::class.java, "UNHANDLED QC PACKET TYPE Size ${x.array().size}") }
}
val forClan = (buffer.get() and 0xFF) == 1
val multiplier: Int = buffer.get()
val offset: Int = buffer.get()
var selection_a_index = -1
var selection_b_index = -1
when(packetType){
QCPacketType.SINGLE -> {
selection_a_index = buffer.short
}
QCPacketType.DOUBLE -> {
buffer.get() //discard
selection_a_index = buffer.get()
buffer.get() //discard
selection_b_index = buffer.get()
}
QCPacketType.UNHANDLED -> SystemLogger.logWarn(this::class.java, "Unhandled packet type, skipping remaining buffer contents.")
}
return Packet.QuickChat(player, selection_a_index, selection_b_index, forClan, multiplier, offset, packetType)
}
},
MAP_REBUILD_STARTED(20) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.NoProcess()
}
},
MAP_REBUILD_FINISHED(110) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
return Packet.NoProcess()
}
},
PLAYER_PREFS_UPDATE(98) {
override fun decode(player: Player, buffer: IoBuffer): Packet {
val prefs = buffer.int
val particleSetting = prefs shr 23
val ambienceEnabled = (prefs shr 22) and 1 == 1
val musicEnabled = (prefs shr 21) and 1 == 1
val soundEnabled = (prefs shr 20) and 1 == 1
val stereo = (prefs shr 19) and 1 == 1
val fog = (prefs shr 16) and 1 == 1
val hdWater = (prefs shr 15) and 1 == 1
val hdLighting = (prefs shr 13) and 1 == 1
val shadowType = (prefs shr 11) and 0x3
val charShadows = (prefs shr 10) and 1 == 1
val manyGroundTextures = (prefs shr 9) and 1 == 1
val flickeringEffects = (prefs shr 8) and 1 == 1
val manyIdleAnims = (prefs shr 7) and 1 == 1
val hdTextures = (prefs shr 6) and 1 == 1
val showGroundDeco = (prefs shr 5) and 1 == 1
val selectiveRoofs = (prefs shr 4) and 1 == 1
val allLayersVisible = (prefs shr 3) and 1 == 1
val brightness = prefs and 0x7;
//added in the above so that we don't have to figure it out later, not doing more
//because we currently have genuinely no use for this information.
return Packet.PlayerPrefsUpdate(player, prefs)
}
}
;
abstract fun decode(player: Player, buffer: IoBuffer): Packet
fun deHash(ifHash: Int) : Pair<Int,Int> {
return Pair(ifHash shr 16, ifHash and 0xFFFF)
}
fun decodeWalkInformation(buffer: IoBuffer, isMinimap: Boolean) : Triple<Boolean,Int,Int> {
val isRunning = buffer.a == 1
var x = buffer.short
var y = buffer.shortA
val steps = (buffer.toByteBuffer().remaining() - if (isMinimap) 14 else 0) shr 1
//derive the final destination by poking the last set of values in the packet (client does its own pathfinding and reports it)
for (i in 0 until steps) {
val offsetX = buffer.a
val offsetY = buffer.s
if (i == steps - 1){
x += offsetX
y += offsetY
}
}
return Triple(isRunning, x, y)
}
companion object {
private val opcodeMap = values().associateBy { it.opcode }
@JvmStatic fun process(player: Player, opcode: Int, buffer: IoBuffer) : Packet {
val decoder = opcodeMap[opcode] ?: return Packet.UnhandledOp()
return try {
decoder.decode(player, buffer)
} catch (e: Exception) {
val sw = StringWriter()
val pw = PrintWriter(sw)
e.printStackTrace(pw)
Packet.DecodingError("Error decoding opcode $opcode/${decoder.name}: $sw")
}
}
}
}
+195
View File
@@ -0,0 +1,195 @@
package core.net.packet.`in`
import core.cache.crypto.ISAACCipher
import core.cache.crypto.ISAACPair
import core.cache.misc.buffer.ByteBufferUtils
import core.game.node.entity.player.Player
import core.net.Constants
import core.net.IoSession
import core.tools.StringUtils
import core.integrations.discord.Discord
import proto.management.JoinClanRequest
import proto.management.PlayerStatusUpdate
import proto.management.RequestContactInfo
import core.ServerConstants
import core.ServerStore
import core.ServerStore.Companion.addToList
import core.ServerStore.Companion.getList
import core.auth.AuthResponse
import core.game.node.entity.player.info.*
import core.game.node.entity.player.info.login.LoginParser
import core.tools.SystemLogger
import core.game.world.GameWorld
import core.game.world.repository.Repository
import core.worker.ManagementEvents.publish
import java.math.BigInteger
import java.nio.BufferUnderflowException
import java.nio.ByteBuffer
object Login {
private const val ENCRYPTION_VERIFICATION_BYTE: Int = 10
private const val NORMAL_LOGIN_OP = 16
private const val RECONNECT_LOGIN_OP = 18
const val CACHE_INDEX_COUNT = 29
fun decodeFromBuffer(buffer: ByteBuffer) : Pair<AuthResponse, LoginInfo?> {
try {
val info = LoginInfo.createDefault()
info.opcode = buffer.get().toInt()
if (buffer.short.toInt() != buffer.remaining()) {
return Pair(AuthResponse.BadSessionID, null)
}
val revision = buffer.int
if (revision != Constants.REVISION) {
return Pair(AuthResponse.Updated, null)
}
if (info.opcode != NORMAL_LOGIN_OP && info.opcode != RECONNECT_LOGIN_OP) {
SystemLogger.logInfo(this::class.java, "Invalid Login Opcode: ${info.opcode}")
return Pair(AuthResponse.InvalidLoginServer, null)
}
noop(buffer)
info.showAds = buffer.get().toInt() == 1
noop(buffer)
info.windowMode = buffer.get().toInt()
info.screenWidth = buffer.short.toInt()
info.screenHeight = buffer.short.toInt()
info.displayMode = buffer.get().toInt()
noop(buffer, 24) //Skip past a bunch of random (actually random) data the client sends
ByteBufferUtils.getString(buffer) //same as above
info.adAffiliateId = buffer.int
info.settingsHash = buffer.int
info.currentPacketCount = buffer.short.toInt()
//Read client-reported CRC sums
for (i in 0 until CACHE_INDEX_COUNT) info.crcSums[i] = buffer.int
val decryptedBuffer = decryptRSABuffer(buffer, ServerConstants.EXPONENT, ServerConstants.MODULUS)
decryptedBuffer.rewind()
if (decryptedBuffer.get().toInt() != ENCRYPTION_VERIFICATION_BYTE) {
return Pair(AuthResponse.UnexpectedError, info)
}
info.isaacPair = produceISAACPairFrom(decryptedBuffer)
info.username = StringUtils.longToString(decryptedBuffer.long)
info.password = ByteBufferUtils.getString(decryptedBuffer)
if (Repository.getPlayerByName(info.username) != null) {
return Pair(AuthResponse.AlreadyOnline, info)
}
return Pair(AuthResponse.Success, info)
} catch (e: Exception) {
SystemLogger.logErr(this::class.java, "Exception encountered during login packet parsing! See stack trace below.")
e.printStackTrace()
return Pair(AuthResponse.UnexpectedError, null)
}
}
private fun produceISAACPairFrom(buffer: ByteBuffer): ISAACPair {
val incomingSeed = IntArray(4)
for(i in incomingSeed.indices) {
incomingSeed[i] = buffer.int
}
val inCipher = ISAACCipher(incomingSeed)
for(i in incomingSeed.indices) {
incomingSeed[i] += 50
}
val outCipher = ISAACCipher(incomingSeed)
return ISAACPair(inCipher, outCipher)
}
@JvmStatic fun decryptRSABuffer(buffer: ByteBuffer, exponent: BigInteger, modulus: BigInteger): ByteBuffer {
return try {
val numBytes = buffer.get().toInt() and 0xFF
val encryptedBytes = ByteArray(numBytes)
buffer.get(encryptedBytes)
val encryptedBigInt = BigInteger(encryptedBytes)
ByteBuffer.wrap(encryptedBigInt.modPow(exponent, modulus).toByteArray())
} catch (e: BufferUnderflowException) {
ByteBuffer.wrap(byteArrayOf(-1))
}
}
private fun noop(buffer: ByteBuffer, amount: Int = 1) {buffer.get(ByteArray(amount))}
fun proceedWith(session: IoSession, details: PlayerDetails, opcode: Int) {
if (!Repository.LOGGED_IN_PLAYERS.contains(details.username))
Repository.LOGGED_IN_PLAYERS.add(details.username)
details.session = session
details.info.translate(UIDInfo(details.ipAddress, "DEPRECATED", "DEPRECATED", "DEPRECATED"))
val archive = ServerStore.getArchive("flagged-ips")
val flaggedIps = archive.getList<String>("ips")
if (flaggedIps.contains(details.ipAddress)) {
Discord.postPlayerAlert(details.username, "Login from flagged IP ${details.ipAddress}")
}
val player = Player(details)
PlayerMonitor.log(player, LogType.IP_LOG, details.ipAddress)
if (canBypassAccountLimitCheck(player)) {
proceedWithAcceptableLogin(session, player, opcode)
} else {
if (checkAccountLimit(details.ipAddress, details.username)) {
proceedWithAcceptableLogin(session, player, opcode)
} else {
session.write(AuthResponse.LoginLimitExceeded)
}
}
}
private fun canBypassAccountLimitCheck(player: Player): Boolean {
return player.rights == Rights.ADMINISTRATOR || player.rights == Rights.PLAYER_MODERATOR
}
private fun proceedWithAcceptableLogin(session: IoSession, player: Player, opcode: Int) {
if (Repository.getPlayerByName(player.name) == null) {
Repository.addPlayer(player)
}
session.lastPing = System.currentTimeMillis()
try {
LoginParser(player.details).initialize(player, opcode == RECONNECT_LOGIN_OP)
sendMSEvents(player.details)
} catch (e: Exception) {
e.printStackTrace()
session.disconnect()
Repository.removePlayer(player)
player.clear(true)
}
}
private fun checkAccountLimit(ipAddress: String, username: String): Boolean {
val archive = ServerStore.getArchive("daily-accounts")
val accounts = archive.getList<String>(ipAddress)
if (username in accounts) return true
if (accounts.size >= ServerConstants.DAILY_ACCOUNT_LIMIT)
return false
archive.addToList(ipAddress, username)
return true
}
private fun sendMSEvents(details: PlayerDetails) {
val statusEvent = PlayerStatusUpdate.newBuilder()
statusEvent.username = details.username
statusEvent.world = GameWorld.settings!!.worldId
statusEvent.notifyFriendsOnly = false
publish(statusEvent.build())
val contactEvent = RequestContactInfo.newBuilder()
contactEvent.username = details.username
contactEvent.world = GameWorld.settings!!.worldId
publish(contactEvent.build())
if (!details.communication.currentClan.isNullOrEmpty() && details.communication.clan == null) {
val clanEvent = JoinClanRequest.newBuilder()
clanEvent.username = details.username
clanEvent.clanName = details.communication.currentClan
publish(clanEvent.build())
}
}
}
@@ -0,0 +1,30 @@
package core.net.packet.`in`
import core.cache.crypto.ISAACCipher
import core.cache.crypto.ISAACPair
class LoginInfo(
var showAds: Boolean, //Unused
var windowMode : Int,
var screenWidth: Int,
var screenHeight: Int,
var displayMode: Int,
var adAffiliateId: Int, //Unused
var settingsHash: Int,
var currentPacketCount: Int,
var username: String,
var password: String,
var isaacPair: ISAACPair,
var opcode: Int,
var crcSums: IntArray
) {
companion object {
fun createDefault(): LoginInfo {
return LoginInfo(false, 0, 0, 0, 0, 0, 0, 0, "", "", ISAACPair(ISAACCipher(intArrayOf()), ISAACCipher(intArrayOf())), 0, IntArray(Login.CACHE_INDEX_COUNT))
}
}
override fun toString(): String {
return "ads:$showAds,wm:$windowMode,sw:$screenWidth,sh:$screenHeight,dm:$displayMode,adid:$adAffiliateId,settings:$settingsHash,pkt:$currentPacketCount,un:$username,pw:$password"
}
}
@@ -0,0 +1,60 @@
package core.net.packet.`in`
import core.game.node.entity.player.Player
sealed class Packet {
data class ItemAction(val player: Player, val optIndex: Int, val itemId: Int, val slot: Int, val iface: Int, val child: Int) : Packet()
data class NpcAction(val player: Player, val optIndex: Int, val npcIndex: Int) : Packet()
data class SceneryAction(val player: Player, val optIndex: Int, val id: Int, val x: Int, val y: Int) : Packet()
data class PlayerAction(val player: Player, val optIndex: Int, val otherIndex: Int) : Packet()
data class GroundItemAction(val player: Player, val optIndex: Int, val id: Int, val x: Int, val y: Int) : Packet()
data class ItemExamine(val player: Player, val id: Int) : Packet()
data class SceneryExamine(val player: Player, val id: Int) : Packet()
data class NpcExamine(val player: Player, val id: Int) : Packet()
data class UseWithNpc(val player: Player, val itemId: Int, val npcIndex: Int, val iface: Int, val slot: Int) : Packet()
data class UseWithPlayer(val player: Player, val itemId: Int, val otherIndex: Int, val iface: Int, val slot: Int) : Packet()
data class UseWithItem(val player: Player, val usedId: Int, val usedWithId: Int, val usedSlot: Int, val usedWithSlot: Int, val usedIface: Int, val usedWithIface: Int, val usedChild: Int, val usedWithChild: Int) : Packet()
data class UseWithScenery(val player: Player, val itemId: Int, val slot: Int, val sceneryId: Int, val x: Int, val y: Int) : Packet()
data class UseWithGroundItem(val player: Player, val usedId: Int, val withId: Int, val iface: Int, val child: Int, val slot: Int, val x: Int, val y: Int) : Packet()
data class IfAction(val player: Player, val opcode: Int, val optIndex: Int, val iface: Int, val child: Int, val slot: Int, val itemId: Int = -1) : Packet()
data class ContinueOption(val player: Player, val iface: Int, val child: Int, val slot: Int, val opcode: Int) : Packet()
data class CloseIface(val player: Player) : Packet()
data class JoinClan(val player: Player, val clanName: String) : Packet()
data class SetClanRank(val player: Player, val username: String, val rank: Int) : Packet()
data class KickFromClan(val player: Player, val username: String) : Packet()
data class AddFriend(val player: Player, val username: String) : Packet()
data class RemoveFriend(val player: Player, val username: String) : Packet()
data class AddIgnore(val player: Player, val username: String) : Packet()
data class RemoveIgnore(val player: Player, val username: String) : Packet()
data class TrackingFocus(val player: Player, val isFocused: Boolean) : Packet()
data class TrackingCameraPos(val player: Player, val x: Int, val y: Int) : Packet()
data class TrackingDisplayUpdate(val player: Player, val windowMode: Int, val screenWidth: Int, val screenHeight: Int, val displayMode: Int) : Packet()
data class TrackingAfkTimeout(val player: Player) : Packet()
data class TrackingMouseClick(val player: Player, val x: Int, val y: Int, val isRightClick: Boolean, val delay: Int) : Packet()
data class ComponentPlayerAction(val player: Player, val otherIndex: Int, val iface: Int, val child: Int) : Packet()
data class ComponentItemAction(val player: Player, val iface: Int, val child: Int, val itemId: Int, val slot: Int) : Packet()
data class ComponentNpcAction(val player: Player, val iface: Int, val child: Int, val npcIndex: Int) : Packet()
data class ComponentSceneryAction(val player: Player, val iface: Int, val child: Int, val sceneryId: Int, val x: Int, val y: Int) : Packet()
data class ComponentGroundItemAction(val player: Player, val iface: Int, val child: Int, val slot: Int, val itemId: Int, val x: Int, val y: Int) : Packet()
data class SlotSwitchMultiComponent(val player: Player, val sourceIface: Int, val sourceChild: Int, val sourceSlot: Int, val destIface: Int, val destChild: Int, val destSlot: Int) : Packet()
data class SlotSwitchSingleComponent(val player: Player, val iface: Int, val child: Int, val sourceSlot: Int, val destSlot: Int, val isInsert: Boolean) : Packet()
data class PacketCountUpdate(val player: Player, val count: Int) : Packet()
data class PrivateMessage(val player: Player, val username: String, val message: String) : Packet()
data class ChatMessage(val player: Player, val effects: Int, val message: String) : Packet()
data class ChatSetting(val player: Player, val public: Int, val private: Int, val trade: Int) : Packet()
data class Command(val player: Player, val commandLine: String) : Packet()
data class GESetOfferItem(val player: Player, val itemId: Int) : Packet()
data class TrackFinished(val player: Player, val trackId: Int) : Packet()
data class ReportAbuse(val player: Player, val target: String, val rule: Int, val modMute: Boolean) : Packet()
data class WorldspaceWalk(val player: Player, val destX: Int, val destY: Int, val isRun: Boolean) : Packet()
data class MinimapWalk(val player: Player, val destX: Int, val destY: Int, val clickedX: Int, val clickedY: Int, val rotation: Int, val isRun: Boolean) : Packet()
data class InteractWalk(val player: Player, val destX: Int, val destY: Int, val isRun: Boolean) : Packet()
data class Ping(val player: Player) : Packet()
data class QuickChat(val player: Player, val indexA: Int, val indexB: Int, val forClan: Boolean, val multiplier: Int, val offset: Int, val type: QCPacketType) : Packet()
data class InputPromptResponse(val player: Player, val response: Any) : Packet()
data class PlayerPrefsUpdate(val player: Player, val prefs: Int) : Packet()
class NoProcess : Packet()
class UnhandledOp : Packet()
data class DecodingError(val message: String) : Packet()
}
@@ -0,0 +1,77 @@
package core.net.packet.`in`
import core.game.node.entity.player.Player
import core.net.packet.IncomingPacket
import core.net.packet.IoBuffer
import core.net.packet.QCRepository
import core.tools.SystemLogger
import core.game.world.GameWorld
/**
* Decodes the quick chat packet
* @author Ceikry
*/
/**
* There's different varieties of the quick chat packet.
* Standard, which is only 3 (sometimes 4) bytes and has no string replacement selections.
* Single-replacement, which is 5 bytes and has one string replacement. The last 3 bytes can and should be converted to a Short.
* Double-replacement, which is 7 bytes and has two string replacements.
* The first byte is whether or not the message is intended for your clan. 0 = public chat, 1 = clan chat
* The second byte will either be 0,1,2,3. This is the number 256 is to be multiplied by.
* The third byte is the index. This can be negative. This number is added to the multiple of 256 determined by the previous byte to get the actual id of the cache file holding the quick chat message.
* In a single replacement, you then have a short (which is 3 bytes in length) that will tell you either the item ID or the index of the selection on the menu.
* In a double replacement, you would have something like 0 1 0 3 instead as the proceeding 4 bytes. The 0s are spacers and the 1 and 3 are the selection indexes for the menus.
*/
class QuickChatPacketHandler : IncomingPacket {
override fun decode(player: Player?, opcode: Int, buffer: IoBuffer?) {
buffer ?: return
val x = buffer.toByteBuffer()
val packetType = when(x.array().size){
3,4 -> QCPacketType.STANDARD
5 -> QCPacketType.SINGLE
7 -> QCPacketType.DOUBLE
else -> QCPacketType.UNHANDLED.also { SystemLogger.logWarn(this::class.java, "UNHANDLED QC PACKET TYPE Size ${x.array().size}") }
}
val forClan = (buffer.get() and 0xFF) == 1
val multiplier: Int = buffer.get()
val offset: Int = buffer.get()
var selection_a_index = -1
var selection_b_index = -1
when(packetType){
QCPacketType.SINGLE -> {
selection_a_index = buffer.short
}
QCPacketType.DOUBLE -> {
buffer.get() //discard
selection_a_index = buffer.get()
buffer.get() //discard
selection_b_index = buffer.get()
}
QCPacketType.UNHANDLED -> SystemLogger.logWarn(this::class.java, "Unhandled packet type, skipping remaining buffer contents.")
}
//Prints the values of each byte in the buffer to server log
//If the world is in dev mode
if(GameWorld.settings?.isDevMode == true) {
SystemLogger.logInfo(this::class.java, "Begin QuickChat Packet Buffer Dump---------")
SystemLogger.logInfo(this::class.java, "Packet Type: ${packetType.name} Chat Type: ${if(forClan) "Clan" else "Public"}")
x?.array()?.forEach {
SystemLogger.logInfo(this::class.java, "$it")
}
SystemLogger.logInfo(this::class.java, "End QuickChat Packet Buffer Dump-----------")
}
QCRepository.sendQC(player,multiplier,offset,packetType,selection_a_index,selection_b_index,forClan)
}
}
enum class QCPacketType{
STANDARD,
SINGLE,
DOUBLE,
UNHANDLED
}
@@ -0,0 +1,35 @@
package core.net.packet.`in`
import core.api.sendDialogue
import core.game.node.entity.player.Player
/**
* Handles an incoming script execution request packet.
*
* @author vddCore
*/
object RunScript {
fun processInput(player: Player, value: Any, script: ((Any) -> Boolean)) {
if (value is Int && value <= 0) return
var input = value
if (player.getAttribute("parseamount", false)) {
input = value.toString().lowercase()
if (!input.matches(Regex("^(\\d+)(k+|m+)?$"))) {
sendDialogue(player, "That doesn't look right. Please try again.")
return
}
input = input.replace("k", "000").replace("m", "000000")
}
try {
script(input)
} catch (_: NumberFormatException) {
sendDialogue(player, "That number's a bit large, don't you think?")
}
}
}
@@ -0,0 +1,23 @@
package core.net.packet.out;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.AccessMaskContext;
/**
* The access mask outgoing packet.
* @author Emperor
*/
public class AccessMask implements OutgoingPacket<AccessMaskContext> {
@Override
public void send(AccessMaskContext context) {
IoBuffer buffer = new IoBuffer(165);
buffer.putLEShort(context.getPlayer().getInterfaceManager().getPacketCount(1));
buffer.putLEShort(context.getLength());
buffer.putInt(context.getInterfaceId() << 16 | context.getChildId());
buffer.putShortA(context.getOffset());
buffer.putIntA(context.getId());
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());context.getPlayer().getSession().write(buffer);
}
}
@@ -0,0 +1,21 @@
package core.net.packet.out;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.AnimateInterfaceContext;
/**
* The animate interface outgoing packet.
* @author Emperor
*/
public class AnimateInterface implements OutgoingPacket<AnimateInterfaceContext> {
@Override
public void send(AnimateInterfaceContext context) {
IoBuffer buffer = new IoBuffer(36);
buffer.putIntB((context.getInterfaceId() << 16) + context.getChildId());
buffer.putLEShort(context.getAnimationId());
buffer.putShortA(context.getPlayer().getInterfaceManager().getPacketCount(1));
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());context.getPlayer().getDetails().getSession().write(buffer);
}
}
@@ -0,0 +1,39 @@
package core.net.packet.out;
import core.game.node.entity.player.Player;
import core.game.node.scenery.Scenery;
import core.game.world.map.Location;
import core.game.world.update.flag.context.Animation;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.AnimateObjectContext;
/**
* Represents the packet used to animate an object.
* @author Vexia
* @date 10/11/2013
*/
public class AnimateObjectPacket implements OutgoingPacket<AnimateObjectContext> {
/**
* Writes the packet.
* @param buffer The buffer.
*/
public static IoBuffer write(IoBuffer buffer, Animation animation) {
Scenery object = animation.getObject();
Location l = object.getLocation();
buffer.put(20);
buffer.putS((l.getChunkOffsetX() << 4) | (l.getChunkOffsetY() & 0x7));
buffer.putS((object.getType() << 2) + (object.getRotation() & 0x3));
buffer.putLEShort(animation.getId());
return buffer;
}
@Override
public void send(AnimateObjectContext context) {
Player player = context.getPlayer();
Scenery object = context.getAnimation().getObject();
IoBuffer buffer = write(UpdateAreaPosition.getBuffer(player, object.getLocation().getChunkBase()), context.getAnimation());
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());player.getSession().write(buffer);
}
}
@@ -0,0 +1,49 @@
package core.net.packet.out;
import core.game.node.entity.player.link.audio.Audio;
import core.game.world.map.Location;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.DefaultContext;
/**
* Sends an audio packet.
* @author Vexia
*/
public class AudioPacket implements OutgoingPacket<DefaultContext> {
public static IoBuffer write(IoBuffer buffer, Audio audio, Location loc) {
if(loc == null) {
buffer.put((byte) 172);
buffer.putShort(audio.getId());
buffer.put((byte) audio.getVolume());
buffer.putShort(audio.getDelay());
} else {
buffer.put((byte) 97);
buffer.put((byte) (loc.getChunkOffsetX() << 4 | loc.getChunkOffsetY()));
buffer.putShort(audio.getId());
buffer.put((byte) (audio.getRadius() << 4 | audio.getVolume() & 7));
buffer.put((byte) audio.getDelay());
}
return buffer;
}
//208 music effect
//4 music
//172 sound effect
@Override
public void send(DefaultContext context) {
final Audio audio = (Audio) context.getObjects()[0];
final Location loc = (Location) context.getObjects()[1];
IoBuffer buffer;
if(loc == null) {
buffer = new IoBuffer();
} else {
buffer = UpdateAreaPosition.getBuffer(context.getPlayer(), loc.getChunkBase());
}
write(buffer, audio, loc);
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());
context.getPlayer().getSession().write(buffer);
}
}
@@ -0,0 +1,78 @@
package core.net.packet.out;
import core.game.node.entity.player.Player;
import core.game.world.map.Region;
import core.game.world.map.RegionChunk;
import core.game.world.map.RegionManager;
import core.game.world.map.build.DynamicRegion;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.PacketHeader;
import core.net.packet.context.DynamicSceneContext;
import core.game.system.config.XteaParser;
import java.util.ArrayList;
import java.util.List;
/**
* @author Emperor
*/
public final class BuildDynamicScene implements OutgoingPacket<DynamicSceneContext> {
@Override
public void send(DynamicSceneContext context) {
IoBuffer buffer = new IoBuffer(214, PacketHeader.SHORT);
List<Integer> regionIds = new ArrayList<>(20);
Player player = context.getPlayer();
buffer.putLEShortA(player.getLocation().getSceneX());
buffer.putLEShortA(player.getLocation().getRegionX());
buffer.putS(player.getLocation().getZ());
buffer.putLEShortA(player.getLocation().getSceneY());
buffer.setBitAccess();
Region r = player.getViewport().getRegion();
RegionChunk[][][] chunks = new RegionChunk[4][13][13];
int baseX = player.getLocation().getRegionX() - 6;
int baseY = player.getLocation().getRegionY() - 6;
for (int z = 0; z < 4; z++) {
for (int x = baseX; x <= player.getLocation().getRegionX() + 6; x++) {
for (int y = baseY; y <= player.getLocation().getRegionY() + 6; y++) {
r = RegionManager.forId((x >> 3) << 8 | (y >> 3));
if (r instanceof DynamicRegion) {
DynamicRegion dr = (DynamicRegion) r;
chunks[z][x - baseX][y - baseY] = dr.getChunks()[z][x - (dr.getX() << 3)][y - (dr.getY() << 3)];
}
}
}
}
for (int plane = 0; plane < 4; plane++) {
for (int offsetX = 0; offsetX < 13; offsetX++) {
for (int offsetY = 0; offsetY < 13; offsetY++) {
RegionChunk c = chunks[plane][offsetX][offsetY];
if (c == null || c.getBase().getX() < 0 || c.getBase().getY() < 0) {
buffer.putBits(1, 0);
continue;
}
int realRegionX = c.getBase().getRegionX();
int realRegionY = c.getBase().getRegionY();
int realPlane = c.getBase().getZ();
int rotation = c.getRotation();
int id = (realRegionX >> 3) << 8 | (realRegionY >> 3);
if (!regionIds.contains(id)) {
regionIds.add(id);
}
buffer.putBits(1, 1);
buffer.putBits(26, (rotation << 1) | (realPlane << 24) | (realRegionX << 14) | (realRegionY << 3));
}
}
}
buffer.setByteAccess();
for (int id : regionIds) {
int[] keys = XteaParser.Companion.getRegionXTEA(id);
buffer.putIntB(keys[0]).putIntB(keys[1]).putIntB(keys[2]).putIntB(keys[3]);
}
buffer.putShort(player.getLocation().getRegionY());
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());context.getPlayer().getSession().write(buffer);
player.getPlayerFlags().setLastSceneGraph(player.getLocation());
}
}
@@ -0,0 +1,40 @@
package core.net.packet.out;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.CSConfigContext;
/**
* The outgoing packet for client script configs.
*
* @author Snickerize
*/
public class CSConfigPacket implements OutgoingPacket<CSConfigContext> {
@Override
public void send(CSConfigContext context) {
IoBuffer buffer = new IoBuffer(65);
buffer.putLEShort(context.getPlayer().getInterfaceManager().getPacketCount(1));
buffer.putC((byte) context.getValue());
buffer.putLEShortA(context.getId());
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());context.getPlayer().getDetails().getSession().write(buffer);
}
/*@Override
public void send(CSConfigContext context) {
IoBuffer buffer = new IoBuffer(115);
buffer.putShort(context.getId());
buffer.putString(context.getTypes());
for (int i = context.getTypes().length() - 1; i >= 0; i--) {
if (context.getTypes().charAt(i) == 's') {
buffer.putString((String) context.getParameters()[i]);
} else {
buffer.putInt(((Number) context.getParameters()[i]).intValue());
}
}
buffer.putInt(context.getValue());
context.getPlayer().getDetails().getSession().write(buffer);
}*/
}
@@ -0,0 +1,44 @@
package core.net.packet.out;
import core.game.node.entity.player.Player;
import core.game.world.map.Location;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.CameraContext;
/**
* Handles the outgoing camera view packets.
* @author Emperor
*/
public final class CameraViewPacket implements OutgoingPacket<CameraContext> {
@Override
public void send(CameraContext context) {
CameraContext.CameraType type = context.getType();
IoBuffer buffer = new IoBuffer(type.opcode());
Location l = Location.create(context.getX(), context.getY(), 0);
Player p = context.getPlayer();
switch (type) {
case ROTATION:
case POSITION:
buffer.putShort(context.getPlayer().getInterfaceManager().getPacketCount(1));
int x = l.getSceneX(p.getPlayerFlags().getLastSceneGraph());
int y = l.getSceneY(p.getPlayerFlags().getLastSceneGraph());
buffer.put(x).put(y).putShort(context.getHeight()).put(context.getSpeed()).put(context.getZoomSpeed());
break;
case SET:
buffer.putLEShort(context.getX())
.putShort(context.getPlayer().getInterfaceManager().getPacketCount(1)).putShort(context.getY());
break;
case SHAKE:
buffer.putShort(context.getPlayer().getInterfaceManager().getPacketCount(1));
buffer.put(l.getX()).put(l.getY()).put(context.getSpeed()).put(context.getZoomSpeed()).putShort(context.getHeight());
break;
case RESET:
buffer.putShort(context.getPlayer().getInterfaceManager().getPacketCount(1));
break;
}
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());p.getSession().write(buffer);
}
}
@@ -0,0 +1,35 @@
package core.net.packet.out;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.game.world.map.Location;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.BuildItemContext;
/**
* Represents the outgoing packet of clearing ground items.
* @author Emperor
*/
public final class ClearGroundItem implements OutgoingPacket<BuildItemContext> {
/**
* Writes the packet.
* @param buffer The buffer.
* @param item The item.
*/
public static IoBuffer write(IoBuffer buffer, Item item) {
Location l = item.getLocation();
buffer.put(240);
buffer.putS((l.getChunkOffsetX() << 4) | (l.getChunkOffsetY() & 0x7)).putShort(item.getId());
return buffer;
}
@Override
public void send(BuildItemContext context) {
Player player = context.getPlayer();
Item item = context.getItem();
IoBuffer buffer = write(UpdateAreaPosition.getBuffer(player, item.getLocation().getChunkBase()), item);
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());player.getSession().write(buffer);
}
}
@@ -0,0 +1,19 @@
package core.net.packet.out;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.PlayerContext;
/**
* Handles the removal of the minimap flag.
* @author Emperor
*/
public final class ClearMinimapFlag implements OutgoingPacket<PlayerContext> {
@Override
public void send(PlayerContext context) {
IoBuffer buffer = new IoBuffer(153);
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());context.getPlayer().getDetails().getSession().write(buffer);
}
}
@@ -0,0 +1,25 @@
package core.net.packet.out;
import core.game.world.map.Location;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.ClearChunkContext;
/**
* Handles the clear region chunk outgoing packet.
* @author Emperor
*/
public final class ClearRegionChunk implements OutgoingPacket<ClearChunkContext> {
@Override
public void send(ClearChunkContext context) {
Location l = context.getPlayer().getPlayerFlags().getLastSceneGraph();
int x = context.getChunk().getCurrentBase().getSceneX(l);
int y = context.getChunk().getCurrentBase().getSceneY(l);
if (x >= 0 && y >= 0 && x < 96 && y < 96) {
IoBuffer buffer = new IoBuffer(112).put(x).putC(y);
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());context.getPlayer().getSession().write(buffer);
}
}
}
@@ -0,0 +1,36 @@
package core.net.packet.out;
import core.game.node.entity.player.Player;
import core.game.node.scenery.Scenery;
import core.game.world.map.Location;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.BuildSceneryContext;
/**
* The clear scenery outgoing packet.
* @author Emperor
*/
public final class ClearScenery implements OutgoingPacket<BuildSceneryContext> {
/**
* Writes the packet.
* @param buffer The buffer.
* @param object The object.
*/
public static IoBuffer write(IoBuffer buffer, Scenery object) {
Location l = object.getLocation();
buffer.put(195) // Opcode
.putC((object.getType() << 2) + (object.getRotation() & 3)).put((l.getChunkOffsetX() << 4) | (l.getChunkOffsetY() & 0x7));
return buffer;
}
@Override
public void send(BuildSceneryContext context) {
Player player = context.getPlayer();
Scenery o = context.getScenery();
IoBuffer buffer = write(UpdateAreaPosition.getBuffer(player, o.getLocation().getChunkBase()), o);
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());player.getSession().write(buffer);
}
}
@@ -0,0 +1,22 @@
package core.net.packet.out;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.InterfaceContext;
/**
* Represents the outgoing packet used for closing an interface.
* @author Emperor
*/
public final class CloseInterface implements OutgoingPacket<InterfaceContext> {
@Override
public void send(InterfaceContext context) {
IoBuffer buffer = new IoBuffer(149);
buffer.putShort(context.getPlayer().getInterfaceManager().getPacketCount(1));
buffer.putShort(context.getWindowId());
buffer.putShort(context.getComponentId());
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());context.getPlayer().getSession().write(buffer);
}
}
@@ -0,0 +1,76 @@
package core.net.packet.out;
import core.game.node.entity.player.Player;
import core.game.system.communication.ClanRepository;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.PacketHeader;
import core.net.packet.context.MessageContext;
import core.tools.StringUtils;
import core.game.bots.AIPlayer;
import java.util.Random;
/**
* Handles communication message packet sending.
* @author Emperor
*/
public final class CommunicationMessage implements OutgoingPacket<MessageContext> {
@Override
public void send(MessageContext context) {
IoBuffer buffer = new IoBuffer(context.getOpcode(), PacketHeader.BYTE);
String message = context.getMessage();
Player player = context.getPlayer();
String other = context.getOther();
switch (context.getOpcode()) {
case MessageContext.SEND_MESSAGE:
byte[] bytes = new byte[256];
int length = StringUtils.encryptPlayerChat(bytes, 0, 0, message.length(), message.getBytes());
buffer.putLong(StringUtils.stringToLong(other));
buffer.put((byte) message.length());
buffer.putBytes(bytes, 0, length);
break;
case MessageContext.RECIEVE_MESSAGE:
bytes = new byte[256];
bytes[0] = (byte) message.length();
length = 1 + StringUtils.encryptPlayerChat(bytes, 0, 1, message.length(), message.getBytes());
player.setAttribute("replyTo", other);
buffer.putLong(StringUtils.stringToLong(other)).putShort(new Random().nextInt(0xFFFF)).putTri(getMessageIndex(player)).put((byte) context.getChatIcon()).putBytes(bytes, 0, length);
break;
case MessageContext.CLAN_MESSAGE:
ClanRepository clan = player.getCommunication().getClan();
if (clan == null) {
System.out.println("Player " + player.getUsername() + " was no longer in clan!");
return;
}
bytes = new byte[256];
bytes[0] = (byte) context.getMessage().length();
length = 1 + StringUtils.encryptPlayerChat(bytes, 0, 1, message.length(), message.getBytes());
buffer.putLong(StringUtils.stringToLong(other));
buffer.put((byte) 0);// it's just read does nothing
buffer.putLong(StringUtils.stringToLong(clan.getName()));
buffer.putShort(new Random().nextInt(0xFFFF));
buffer.putTri(getMessageIndex(player));
buffer.put((byte) context.getChatIcon()); // rights
buffer.putBytes(bytes, 0, length);
break;
}
if(player.isArtificial()){
((AIPlayer) player).handleIncomingChat(context);
}
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());player.getSession().write(buffer);
}
/**
* Gets the message index.
* @param p The player.
* @return The message index.
*/
private static int getMessageIndex(Player p) {
int count = p.getAttribute("pm_index", 0) + 1;
p.setAttribute("pm_index", count);
return count;
}
}
@@ -0,0 +1,29 @@
package core.net.packet.out;
import core.net.packet.context.ConfigContext;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
/**
* The config outgoing packet.
* @author Emperor
*/
public class Config implements OutgoingPacket<ConfigContext> {
@Override
public void send(ConfigContext context) {
IoBuffer buffer;
if (context.getValue() < Byte.MIN_VALUE || context.getValue() > Byte.MAX_VALUE) {
buffer = new IoBuffer(226);
buffer.putInt(context.getValue());
buffer.putShortA(context.getId());
} else {
buffer = new IoBuffer(60);
buffer.putShortA(context.getId());
buffer.putC(context.getValue());
}
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());if(!context.getPlayer().isArtificial()) {
context.getPlayer().getDetails().getSession().write(buffer);
}
}
}
@@ -0,0 +1,35 @@
package core.net.packet.out;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.game.world.map.Location;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.BuildItemContext;
/**
* Represents the outgoing packet of constructing a ground item.
* @author Emperor
*/
public final class ConstructGroundItem implements OutgoingPacket<BuildItemContext> {
/**
* Writes the packet.
* @param buffer The buffer.
* @param item The item.
*/
public static IoBuffer write(IoBuffer buffer, Item item) {
Location l = item.getLocation();
buffer.put(33);
buffer.putLEShort(item.getId()).put((l.getChunkOffsetX() << 4) | (l.getChunkOffsetY() & 0x7)).putShortA(item.getAmount());
return buffer;
}
@Override
public void send(BuildItemContext context) {
Player player = context.getPlayer();
Item item = context.getItem();
IoBuffer buffer = write(UpdateAreaPosition.getBuffer(player, item.getLocation().getChunkBase()), item);
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());player.getDetails().getSession().write(buffer);
}
}
@@ -0,0 +1,36 @@
package core.net.packet.out;
import core.game.node.entity.player.Player;
import core.game.node.scenery.Scenery;
import core.game.world.map.Location;
import core.net.packet.IoBuffer;
import core.net.packet.OutgoingPacket;
import core.net.packet.context.BuildSceneryContext;
/**
* The construct scenery packet.
* @author Emperor
*/
public final class ConstructScenery implements OutgoingPacket<BuildSceneryContext> {
/**
* Writes the packet.
* @param buffer The buffer.
* @param object The object.
*/
public static IoBuffer write(IoBuffer buffer, Scenery object) {
Location l = object.getLocation();
buffer.put(179).putA((object.getType() << 2) | (object.getRotation() & 0x3)).put((l.getChunkOffsetX() << 4) | (l.getChunkOffsetY() & 0x7)).putShortA(object.getId());
return buffer;
}
@Override
public void send(BuildSceneryContext context) {
Player player = context.getPlayer();
Scenery o = context.getScenery();
IoBuffer buffer = write(UpdateAreaPosition.getBuffer(player, o.getLocation().getChunkBase()), o);
buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());player.getSession().write(buffer);
}
}

Some files were not shown because too many files have changed in this diff Show More