|
|
|
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
|
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
|
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
|
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
|
|
|
// Magicbane Emulator Project © 2013 - 2022
|
|
|
|
// www.magicbane.com
|
|
|
|
|
|
|
|
|
|
|
|
package engine.net;
|
|
|
|
|
|
|
|
import engine.exception.SerializationException;
|
|
|
|
import engine.server.MBServerStatics;
|
|
|
|
import engine.util.StringUtils;
|
|
|
|
import org.pmw.tinylog.Logger;
|
|
|
|
|
|
|
|
import java.nio.BufferOverflowException;
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class represents the NetMsgs set to/from the SBClient and in between
|
|
|
|
* MBServer Server Suite components. Note that since the NetMsgs sent to/from
|
|
|
|
* the SBClient do NOT include a MsgLen or DataLen parameter, special
|
|
|
|
* serialization/deserialization must be implemented.
|
|
|
|
*/
|
|
|
|
public abstract class AbstractNetMsg {
|
|
|
|
|
|
|
|
private static ConcurrentHashMap<Protocol, NetMsgStat> stats = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_HIGH);
|
|
|
|
protected final Protocol protocolMsg;
|
|
|
|
private AbstractConnection origin;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is the general purpose constructor.
|
|
|
|
*
|
|
|
|
* @param protocolMsg
|
|
|
|
*/
|
|
|
|
protected AbstractNetMsg(Protocol protocolMsg) {
|
|
|
|
super();
|
|
|
|
this.protocolMsg = protocolMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected AbstractNetMsg(Protocol protocolMsg, AbstractConnection origin) {
|
|
|
|
super();
|
|
|
|
this.protocolMsg = protocolMsg;
|
|
|
|
this.origin = origin;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected AbstractNetMsg(Protocol protocolMsg, AbstractNetMsg msg) {
|
|
|
|
super();
|
|
|
|
this.protocolMsg = protocolMsg;
|
|
|
|
this.origin = msg.origin;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This constructor is used by NetMsgFactory. It attempts to deserialize the
|
|
|
|
* ByteBuffer into a message. If a BufferUnderflow occurs (based on reading
|
|
|
|
* past the limit) then this constructor Throws that Exception to the
|
|
|
|
* caller.
|
|
|
|
*
|
|
|
|
* @param reader
|
|
|
|
*/
|
|
|
|
protected AbstractNetMsg(Protocol protocolMsg, AbstractConnection origin,
|
|
|
|
ByteBufferReader reader) {
|
|
|
|
this.protocolMsg = protocolMsg;
|
|
|
|
this.origin = origin;
|
|
|
|
|
|
|
|
// Call the subclass specific deserializer
|
|
|
|
try {
|
|
|
|
this._deserialize(reader);
|
|
|
|
} catch (NullPointerException e) {
|
|
|
|
Logger.error(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void allocHeader(ByteBufferWriter writer, int bytes) {
|
|
|
|
byte zero = 0; // prevents the int->byte cast
|
|
|
|
for (int h = 0; h < bytes; ++h) {
|
|
|
|
writer.put(zero);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deserializes the subclass specific items from the supplied
|
|
|
|
* ByteBufferReader
|
|
|
|
*
|
|
|
|
* @param reader
|
|
|
|
*/
|
|
|
|
protected abstract void _deserialize(ByteBufferReader reader);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Serializes the subclass specific items to the supplied ByteBufferWriter
|
|
|
|
*
|
|
|
|
* @param writer
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
protected abstract void _serialize(ByteBufferWriter writer)
|
|
|
|
throws SerializationException;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempts to serialize this NetMsg into a ByteBuffer. ByteBuffer is
|
|
|
|
* obtained from a pool, so to retain max efficiency, the caller needs to
|
|
|
|
* return this BB to the pool. Header size and layout is entirely defined by
|
|
|
|
* the subclass of AbstractNetMsg
|
|
|
|
*
|
|
|
|
* @return a ByteBuffer
|
|
|
|
*/
|
|
|
|
public final ByteBuffer serialize() {
|
|
|
|
|
|
|
|
NetMsgStat stat;
|
|
|
|
|
|
|
|
if (!AbstractNetMsg.stats.containsKey(this.protocolMsg)) {
|
|
|
|
stat = new NetMsgStat(this.protocolMsg, this.getPowerOfTwoBufferSize());
|
|
|
|
AbstractNetMsg.stats.put(this.protocolMsg, stat);
|
|
|
|
} else
|
|
|
|
stat = AbstractNetMsg.stats.get(this.protocolMsg);
|
|
|
|
int lowerPow = stat.getMax();
|
|
|
|
int upperPow = lowerPow + 4;
|
|
|
|
|
|
|
|
ByteBuffer bb = null;
|
|
|
|
|
|
|
|
int startPos = 0;
|
|
|
|
ByteBufferWriter writer = null;
|
|
|
|
|
|
|
|
for (int i = lowerPow; i < upperPow; ++i) {
|
|
|
|
|
|
|
|
// get an appropriate sized BB from pool
|
|
|
|
|
|
|
|
bb = Network.byteBufferPool.getBuffer(i);
|
|
|
|
|
|
|
|
// Mark start position
|
|
|
|
|
|
|
|
startPos = bb.position();
|
|
|
|
|
|
|
|
// Make a writer
|
|
|
|
|
|
|
|
writer = new ByteBufferWriter(bb); // FIXME inefficient to
|
|
|
|
|
|
|
|
// Set aside header here.
|
|
|
|
|
|
|
|
AbstractNetMsg.allocHeader(writer, this.getHeaderSize());
|
|
|
|
|
|
|
|
// Now serialize the object's specifics
|
|
|
|
|
|
|
|
try {
|
|
|
|
this._serialize(writer);
|
|
|
|
|
|
|
|
//Serialize successful, update NetMsgStat
|
|
|
|
|
|
|
|
stat.updateStat(i);
|
|
|
|
|
|
|
|
} catch (BufferOverflowException boe) {
|
|
|
|
Logger.error("BufferSize PowerOfTwo: " + i
|
|
|
|
+ " is too small for " + protocolMsg != null ? protocolMsg.name() : this.getClass().getName() + ", trying again with " + (i + 1));
|
|
|
|
|
|
|
|
//Return buffer.
|
|
|
|
|
|
|
|
Network.byteBufferPool.putBuffer(bb);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
//Return buffer.
|
|
|
|
Logger.error(e);
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
|
|
Network.byteBufferPool.putBuffer(bb);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This shouldn't throw any errors since this part of the BB has
|
|
|
|
// already been allocated
|
|
|
|
|
|
|
|
this.writeHeaderAt(startPos, writer);
|
|
|
|
return writer.getBb();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we get here, its not a successful serialization and lastError
|
|
|
|
// should be set
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function allows for setting other than default initial Buffer size for
|
|
|
|
* the Serializer. Override and return the size of the buffer in power of
|
|
|
|
* two bytes.
|
|
|
|
* <p>
|
|
|
|
* Example, if you would like a buffer of 65535, then return 16 from this
|
|
|
|
* value since 2^16 = 65535
|
|
|
|
*
|
|
|
|
* @return the power to raise two to.
|
|
|
|
*/
|
|
|
|
protected int getPowerOfTwoBufferSize() {
|
|
|
|
return (10); // 2^10 == 1024
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Forces subclass to define how large (in bytes) the message's header is.
|
|
|
|
*
|
|
|
|
* @return the length (in bytes) of this message type's header.
|
|
|
|
*/
|
|
|
|
protected abstract int getHeaderSize();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Forces subclasses to implement how to write its own header into a
|
|
|
|
* byteBuffer
|
|
|
|
*
|
|
|
|
* @param startPos - starting position for the header write
|
|
|
|
* @param writer - ByteBufferWriter to write the header to.
|
|
|
|
*/
|
|
|
|
protected abstract void writeHeaderAt(int startPos, ByteBufferWriter writer);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return The protocolMsg of this Msg.
|
|
|
|
*/
|
|
|
|
public Protocol getProtocolMsg() {
|
|
|
|
return protocolMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return The protocolMsg As a string.
|
|
|
|
*/
|
|
|
|
public String getOpcodeAsString() {
|
|
|
|
return StringUtils.toHexString(protocolMsg.opcode);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the origin
|
|
|
|
*/
|
|
|
|
public AbstractConnection getOrigin() {
|
|
|
|
return origin;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setOrigin(AbstractConnection conn) {
|
|
|
|
this.origin = conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|