Merge branch 'master' into fix-tools

# Conflicts:
#	pom.xml
This commit is contained in:
P0nk
2021-07-11 14:53:11 +02:00
16 changed files with 755 additions and 94 deletions
+5
View File
@@ -1,5 +1,7 @@
package config;
import java.util.*;
public class ServerConfig {
//Thread Tracker Configuration
public boolean USE_THREAD_TRACKER;
@@ -163,6 +165,7 @@ public class ServerConfig {
public long NAME_CHANGE_COOLDOWN;
public long WORLD_TRANSFER_COOLDOWN=NAME_CHANGE_COOLDOWN;//Cooldown for world tranfers, default is same as name change (30 days).
public boolean INSTANT_NAME_CHANGE;
public int REBIRTH_NPC_ID;
//Dangling Items/Locks Configuration
public int ITEM_EXPIRE_TIME ;
@@ -304,4 +307,6 @@ public class ServerConfig {
//Event End Timestamp
public long EVENT_END_TIMESTAMP;
//Custom NPC overrides. List of NPC IDs.
public Map<String, String> NPCS_SCRIPTABLE = new HashMap<>();
}
@@ -1,24 +0,0 @@
package constants.game;
/**
* @brief ScriptableNPCConstants
* @author GabrielSin <[email protected]>
* @date 16/09/2018
*
* Adaptations to use Pair and Set, in order to suit a one-packet marshall.
* Adapted by Ronan
*/
import java.util.HashSet;
import java.util.Set;
import tools.Pair;
public class ScriptableNPCConstants {
public static final Set<Pair<Integer, String>> SCRIPTABLE_NPCS = new HashSet<Pair<Integer, String>>(){{
//add(new Pair<>(9200000, "Cody"));
add(new Pair<>(9001105, "Grandpa Moon Bunny"));
}};
}
@@ -0,0 +1,81 @@
package net.packet;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import java.awt.*;
public class ByteBufInPacket implements InPacket {
private final ByteBuf byteBuf;
public ByteBufInPacket(ByteBuf byteBuf) {
this.byteBuf = byteBuf;
}
@Override
public byte[] getBytes() {
return ByteBufUtil.getBytes(byteBuf);
}
@Override
public byte readByte() {
return byteBuf.readByte();
}
@Override
public short readShort() {
return byteBuf.readShortLE();
}
@Override
public int readInt() {
return byteBuf.readIntLE();
}
@Override
public long readLong() {
return byteBuf.readLongLE();
}
@Override
public Point readPoint() {
final short x = byteBuf.readShortLE();
final short y = byteBuf.readShortLE();
return new Point(x, y);
}
@Override
public String readString() {
short length = readShort();
byte[] stringBytes = new byte[length];
byteBuf.readBytes(stringBytes);
return new String(stringBytes, STRING_CHARSET);
}
@Override
public byte[] readBytes(int numberOfBytes) {
byte[] bytes = new byte[numberOfBytes];
byteBuf.readBytes(bytes);
return bytes;
}
@Override
public void skip(int numberOfBytes) {
byteBuf.skipBytes(numberOfBytes);
}
@Override
public int available() {
return byteBuf.readableBytes();
}
@Override
public void seek(int byteOffset) {
byteBuf.readerIndex(byteOffset);
}
@Override
public int getPosition() {
return byteBuf.readerIndex();
}
}
@@ -0,0 +1,83 @@
package net.packet;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import net.jcip.annotations.NotThreadSafe;
import net.opcodes.SendOpcode;
import java.awt.*;
@NotThreadSafe
public class ByteBufOutPacket implements OutPacket {
private final ByteBuf byteBuf;
public ByteBufOutPacket(SendOpcode op) {
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeShortLE((short) op.getValue());
this.byteBuf = byteBuf;
}
public ByteBufOutPacket(SendOpcode op, int initialCapacity) {
ByteBuf byteBuf = Unpooled.buffer(initialCapacity);
byteBuf.writeShortLE((short) op.getValue());
this.byteBuf = byteBuf;
}
@Override
public byte[] getBytes() {
return ByteBufUtil.getBytes(byteBuf);
}
@Override
public void writeByte(byte value) {
byteBuf.writeByte(value);
}
@Override
public void writeByte(int value) {
writeByte((byte) value);
}
@Override
public void writeBytes(byte[] value) {
byteBuf.writeBytes(value);
}
@Override
public void writeShort(int value) {
byteBuf.writeShortLE(value);
}
@Override
public void writeInt(int value) {
byteBuf.writeIntLE(value);
}
@Override
public void writeLong(long value) {
byteBuf.writeLongLE(value);
}
@Override
public void writeBoolean(boolean value) {
byteBuf.writeByte(value ? 1 : 0);
}
@Override
public void writeString(String value) {
writeShort((short) value.length());
writeBytes(value.getBytes(STRING_CHARSET));
}
@Override
public void writePoint(Point value) {
writeShort((short) value.getX());
writeShort((short) value.getY());
}
@Override
public void skip(int numberOfBytes) {
writeBytes(new byte[numberOfBytes]);
}
}
+17
View File
@@ -0,0 +1,17 @@
package net.packet;
import java.awt.*;
public interface InPacket extends Packet {
byte readByte();
short readShort();
int readInt();
long readLong();
Point readPoint();
String readString();
byte[] readBytes(int numberOfBytes);
void skip(int numberOfBytes);
int available();
void seek(int byteOffset);
int getPosition();
}
+16
View File
@@ -0,0 +1,16 @@
package net.packet;
import java.awt.*;
public interface OutPacket extends Packet {
void writeByte(byte value);
void writeByte(int value);
void writeBytes(byte[] value);
void writeShort(int value);
void writeInt(int value);
void writeLong(long value);
void writeBoolean(boolean value);
void writeString(String value);
void writePoint(Point value);
void skip(int numberOfBytes);
}
+10
View File
@@ -0,0 +1,10 @@
package net.packet;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public interface Packet {
Charset STRING_CHARSET = StandardCharsets.US_ASCII;
byte[] getBytes();
}
@@ -65,6 +65,8 @@ public final class NPCTalkHandler extends AbstractMaplePacketHandler {
NPCScriptManager.getInstance().start(c, npc.getId(), "gachapon", null);
} else if (npc.getName().endsWith("Maple TV")) {
NPCScriptManager.getInstance().start(c, npc.getId(), "mapleTV", null);
} else if (YamlConfig.config.server.USE_REBIRTH_SYSTEM && npc.getId() == YamlConfig.config.server.REBIRTH_NPC_ID) {
NPCScriptManager.getInstance().start(c, npc.getId(), "rebirth", null);
} else {
boolean hasNpcScript = NPCScriptManager.getInstance().start(c, npc.getId(), oid, null);
if (!hasNpcScript) {
@@ -26,7 +26,6 @@ import client.inventory.*;
import client.keybind.MapleKeyBinding;
import config.YamlConfig;
import constants.game.GameConstants;
import constants.game.ScriptableNPCConstants;
import net.AbstractMaplePacketHandler;
import net.server.PlayerBuffValueHolder;
import net.server.Server;
@@ -55,6 +54,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
@@ -407,9 +407,23 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
eim.registerPlayer(player);
}
}
// Tell the client to use the custom scripts available for the NPCs provided, instead of the WZ entries.
if (YamlConfig.config.server.USE_NPCS_SCRIPTABLE) {
c.announce(MaplePacketCreator.setNPCScriptable(ScriptableNPCConstants.SCRIPTABLE_NPCS));
// Create a copy to prevent always adding entries to the server's list.
Map<Integer, String> npcsIds = YamlConfig.config.server.NPCS_SCRIPTABLE
.entrySet().stream().collect(Collectors.toMap(
entry -> Integer.parseInt(entry.getKey()),
Entry::getValue
));
// Any npc be specified as the rebirth npc. Allow the npc to use custom scripts explicitly.
if (YamlConfig.config.server.USE_REBIRTH_SYSTEM) {
npcsIds.put(YamlConfig.config.server.REBIRTH_NPC_ID, "Rebirth");
}
c.announce(MaplePacketCreator.setNPCScriptable(npcsIds));
}
if(newcomer) player.setLoginTime(System.currentTimeMillis());
+31 -23
View File
@@ -8327,17 +8327,25 @@ public class MaplePacketCreator {
mplew.writeInt(transition);
return mplew.getPacket();
}
public static byte[] setNPCScriptable(Set<Pair<Integer, String>> scriptNpcDescriptions) { // thanks to GabrielSin
/**
* Makes the NPCs provided set as scriptable, informing the client to search for js scripts for these NPCs even
* if they already have entries within the wz files.
*
* @param scriptableNpcIds Ids of npcs to enable scripts for.
* @return a packet which makes the npc's provided scriptable.
*/
public static byte[] setNPCScriptable(Map<Integer, String> scriptableNpcIds) { // thanks to GabrielSin
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.SET_NPC_SCRIPTABLE.getValue());
mplew.write(scriptNpcDescriptions.size());
for (Pair<Integer, String> p : scriptNpcDescriptions) {
mplew.writeInt(p.getLeft());
mplew.writeMapleAsciiString(p.getRight());
mplew.write(scriptableNpcIds.size());
scriptableNpcIds.forEach((id, name) -> {
mplew.writeInt(id);
// The client needs a name for the npc conversation, which is displayed under etc when the npc has a quest available.
mplew.writeMapleAsciiString(name);
mplew.writeInt(0); // start time
mplew.writeInt(Integer.MAX_VALUE); // end time
}
});
return mplew.getPacket();
}
@@ -8383,17 +8391,17 @@ public class MaplePacketCreator {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.TOURNAMENT_SET_PRIZE.getValue());
//0 = "You have failed the set the prize. Please check the item number again."
//1 = "You have successfully set the prize."
mplew.write(bSetPrize);
mplew.write(bHasPrize);
if(bHasPrize != 0)
{
mplew.writeInt(nItemID1);
mplew.writeInt(nItemID2);
}
//0 = "You have failed the set the prize. Please check the item number again."
//1 = "You have successfully set the prize."
mplew.write(bSetPrize);
mplew.write(bHasPrize);
if(bHasPrize != 0)
{
mplew.writeInt(nItemID1);
mplew.writeInt(nItemID2);
}
return mplew.getPacket();
}
@@ -8402,11 +8410,11 @@ public class MaplePacketCreator {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.TOURNAMENT_UEW.getValue());
//Is this a bitflag o.o ?
//2 = "You have reached the finals by default."
//4 = "You have reached the semifinals by default."
//8 or 16 = "You have reached the round of %n by default." | Encodes nState as %n ?!
mplew.write(nState);
//Is this a bitflag o.o ?
//2 = "You have reached the finals by default."
//4 = "You have reached the semifinals by default."
//8 or 16 = "You have reached the round of %n by default." | Encodes nState as %n ?!
mplew.write(nState);
return mplew.getPacket();
}
@@ -0,0 +1,192 @@
package net.packet;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.awt.*;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ByteBufInPacketTest {
private ByteBuf byteBuf;
private InPacket inPacket;
@BeforeEach
void reset() {
this.byteBuf = Unpooled.buffer();
this.inPacket = new ByteBufInPacket(byteBuf);
}
private void givenWrittenBytes(int... bytes) {
for (int b : bytes) {
byteBuf.writeByte(b);
}
}
@Test
void readByte() {
final byte writtenByte = 123;
byteBuf.writeByte(writtenByte);
byte readByte = inPacket.readByte();
assertEquals(writtenByte, readByte);
}
@Test
void readShort() {
final short writtenShort = 12_345;
byteBuf.writeShortLE(writtenShort);
short readShort = inPacket.readShort();
assertEquals(writtenShort, readShort);
}
@Test
void readInt() {
final int writtenInt = 1_234_567_890;
byteBuf.writeIntLE(writtenInt);
int readInt = inPacket.readInt();
assertEquals(writtenInt, readInt);
}
@Test
void readLong() {
final long writtenLong = 9_223_372_036_854_775_807L;
byteBuf.writeLongLE(writtenLong);
long readLong = inPacket.readLong();
assertEquals(writtenLong, readLong);
}
@Test
void readPoint() {
final Point writtenPoint = new Point(111, 222);
byteBuf.writeShortLE((short) writtenPoint.getX());
byteBuf.writeShortLE((short) writtenPoint.getY());
Point readPoint = inPacket.readPoint();
assertEquals(writtenPoint, readPoint);
}
@Test
void readString() {
final String writtenString = "You have gained experience (+3200)";
byteBuf.writeShortLE(writtenString.length());
byte[] writtenStringBytes = writtenString.getBytes(Packet.STRING_CHARSET);
byteBuf.writeBytes(writtenStringBytes);
String readString = inPacket.readString();
assertEquals(writtenString, readString);
}
@Test
void readBytes() {
givenWrittenBytes(10, 11, 12, 13, 14, 15);
byte[] byteBatch1 = inPacket.readBytes(1);
assertEquals(1, byteBatch1.length);
assertEquals(10, byteBatch1[0]);
byte[] byteBatch2 = inPacket.readBytes(2);
assertEquals(2, byteBatch2.length);
assertEquals(11, byteBatch2[0]);
assertEquals(12, byteBatch2[1]);
byte[] byteBatch3 = inPacket.readBytes(3);
assertEquals(3, byteBatch3.length);
assertEquals(13, byteBatch3[0]);
assertEquals(14, byteBatch3[1]);
assertEquals(15, byteBatch3[2]);
}
@Test
void skip() {
givenWrittenBytes(20, 21, 22, 23, 24, 25);
byte firstByte = inPacket.readByte();
assertEquals(20, firstByte);
inPacket.skip(3);
byte fifthByte = inPacket.readByte();
assertEquals(24, fifthByte);
}
@Test
void available() {
givenWrittenBytes(30, 31, 32, 33, 34, 35);
assertEquals(6, inPacket.available());
inPacket.readByte();
assertEquals(5, inPacket.available());
inPacket.readInt();
assertEquals(1, inPacket.available());
}
@Test
void seek() {
givenWrittenBytes(40, 41, 42, 43, 44, 45);
inPacket.seek(2);
assertEquals(4, inPacket.available());
byte byteAtSeek = inPacket.readByte();
assertEquals(42, byteAtSeek);
inPacket.seek(0);
byte byteAtReset = inPacket.readByte();
assertEquals(40, byteAtReset);
}
@Test
void getPosition() {
givenWrittenBytes(50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60);
assertEquals(0, inPacket.getPosition());
inPacket.readByte();
assertEquals(1, inPacket.getPosition());
inPacket.readShort();
assertEquals(3, inPacket.getPosition());
inPacket.readInt();
assertEquals(7, inPacket.getPosition());
inPacket.seek(5);
assertEquals(5, inPacket.getPosition());
}
@Test
void getBytes() {
givenWrittenBytes(20, 19, 21, 18, 22);
byte[] bytes = inPacket.getBytes();
assertArrayEquals(new byte[]{20, 19, 21, 18, 22}, bytes);
}
@Test
void whenGetBytes_shouldBeRepeatable() {
givenWrittenBytes(1, 2, 3, 4, 5);
byte[] bytes = inPacket.getBytes();
assertEquals(5, bytes.length);
byte[] sameBytes = inPacket.getBytes();
assertEquals(5, sameBytes.length);
assertArrayEquals(bytes, sameBytes);
}
}
@@ -0,0 +1,206 @@
package net.packet;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.opcodes.SendOpcode;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.awt.*;
import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ByteBufOutPacketTest {
private ByteBufOutPacket outPacket;
@BeforeEach
void reset() {
outPacket = new ByteBufOutPacket(SendOpcode.ADMIN_SHOP); // Any opcode will do
}
private static ByteBuf wrapExplicitlyWrittenBytes(OutPacket outPacket) {
byte[] packetBytes = outPacket.getBytes();
ByteBuf byteBuf = Unpooled.copiedBuffer(packetBytes);
byteBuf.readShortLE(); // Skip over opcode
return byteBuf;
}
@Test
void whenInstantiatingNew_shouldWriteOpcode() {
byte[] packetBytes = new ByteBufOutPacket(SendOpcode.NPC_TALK).getBytes();
assertEquals(2, packetBytes.length);
}
@Test
void getBytes() {
ByteBufOutPacket outPacket = new ByteBufOutPacket(SendOpcode.PING); // This opcode has value 0x11 = 17 in decimal
outPacket.writeByte(10);
outPacket.writeByte(20);
outPacket.writeByte(30);
byte[] bytes = outPacket.getBytes();
assertArrayEquals(new byte[]{(byte) 17, (byte) 0, (byte) 10, (byte) 20, (byte) 30}, bytes);
}
@Test
void writeByte() {
final byte writtenByte = 19;
outPacket.writeByte(writtenByte);
ByteBuf wrapped = wrapExplicitlyWrittenBytes(outPacket);
byte readByte = wrapped.readByte();
assertEquals(writtenByte, readByte);
}
@Test
void writeByteFromInt() {
final int writtenInt = 123;
outPacket.writeByte(writtenInt);
ByteBuf wrapped = wrapExplicitlyWrittenBytes(outPacket);
byte readByte = wrapped.readByte();
assertEquals(writtenInt, readByte);
}
@Test
void whenWritingByteFromInt_shouldOnlyWrite1Byte() {
final int writtenInt = Integer.MAX_VALUE;
outPacket.writeByte(writtenInt);
byte[] bytes = outPacket.getBytes();
assertEquals(2 + 1, bytes.length); // 2 for opcode
}
@Test
void writeBytes() {
byte[] writtenBytes = {101, 102, 103};
outPacket.writeBytes(writtenBytes);
ByteBuf wrapped = wrapExplicitlyWrittenBytes(outPacket);
assertEquals(101, wrapped.readByte());
assertEquals(102, wrapped.readByte());
assertEquals(103, wrapped.readByte());
}
@Test
void writeShort() {
final short writtenShort = 4312;
outPacket.writeShort(writtenShort);
ByteBuf wrapped = wrapExplicitlyWrittenBytes(outPacket);
short readShort = wrapped.readShortLE();
assertEquals(writtenShort, readShort);
}
@Test
void whenWritingShortFromInt_shouldOnlyWrite2Bytes() {
final int writtenInt = Integer.MAX_VALUE;
outPacket.writeShort(writtenInt);
byte[] bytes = outPacket.getBytes();
assertEquals(2 + 2, bytes.length); // 2 for opcode
}
@Test
void writeShortFromInt() {
final int writtenInt = 34_567;
outPacket.writeShort(writtenInt);
ByteBuf wrapped = wrapExplicitlyWrittenBytes(outPacket);
short readShort = wrapped.readShortLE();
assertEquals((short) writtenInt, readShort);
}
@Test
void writeInt() {
final int writtenInt = 1_010_101_010;
outPacket.writeInt(writtenInt);
ByteBuf wrapped = wrapExplicitlyWrittenBytes(outPacket);
int readInt = wrapped.readIntLE();
assertEquals(writtenInt, readInt);
}
@Test
void writeLong() {
final long writtenLong = 100_200_300_400_500_600L;
outPacket.writeLong(writtenLong);
ByteBuf wrapped = wrapExplicitlyWrittenBytes(outPacket);
long readLong = wrapped.readLongLE();
assertEquals(writtenLong, readLong);
}
@Test
void writeBoolean_true() {
outPacket.writeBoolean(true);
ByteBuf wrapped = wrapExplicitlyWrittenBytes(outPacket);
byte readByte = wrapped.readByte();
assertEquals(1, readByte);
}
@Test
void writeBoolean_false() {
outPacket.writeBoolean(false);
ByteBuf wrapped = wrapExplicitlyWrittenBytes(outPacket);
byte readByte = wrapped.readByte();
assertEquals(0, readByte);
}
@Test
void writeString() {
final String writtenString = "You've been weakened, making you unable to jump.";
outPacket.writeString(writtenString);
ByteBuf wrapped = wrapExplicitlyWrittenBytes(outPacket);
int length = wrapped.readShortLE();
byte[] stringBytes = new byte[length];
wrapped.readBytes(stringBytes);
String readString = new String(stringBytes, StandardCharsets.US_ASCII);
assertEquals(writtenString, readString);
}
@Test
void writePoint() {
final Point writtenPoint = new Point(23, 42);
outPacket.writePoint(writtenPoint);
ByteBuf wrapped = wrapExplicitlyWrittenBytes(outPacket);
short readX = wrapped.readShortLE();
short readY = wrapped.readShortLE();
assertEquals((short) writtenPoint.getX(), readX);
assertEquals((short) writtenPoint.getY(), readY);
}
@Test
void whenSkipping_shouldWriteZeroes() {
final byte firstWrittenByte = 9;
final byte secondWrittenByte = 11;
outPacket.writeByte(firstWrittenByte);
outPacket.skip(2);
outPacket.writeByte(secondWrittenByte);
ByteBuf wrapped = wrapExplicitlyWrittenBytes(outPacket);
assertEquals(firstWrittenByte, wrapped.readByte());
assertEquals(0, wrapped.readByte());
assertEquals(0, wrapped.readByte());
assertEquals(secondWrittenByte, wrapped.readByte());
}
}