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

View File

@@ -319,6 +319,7 @@ server:
NAME_CHANGE_COOLDOWN: 2592000000 # (30*24*60*60*1000) Cooldown for name changes, default (GMS) is 30 days.
WORLD_TRANSFER_COOLDOWN: 2592000000 # (30*24*60*60*1000) Cooldown for world tranfers, default is same as name change (30 days).
INSTANT_NAME_CHANGE: false #Whether or not to wait for server restart to apply name changes. Does on reconnect otherwise (requires queries on every login).
REBIRTH_NPC_ID: 9010021 #ID of the NPC that should be replaced with the rebirth mechanic, if enabled.
#Dangling Items/Locks Configuration
ITEM_EXPIRE_TIME: 180000 # (3 * 60 * 1000) Time before items start disappearing. Recommended to be set up to 3 minutes.
@@ -460,3 +461,7 @@ server:
#Event End Timestamp
EVENT_END_TIMESTAMP: 1428897600000
#Any NPC ids that should search for a js override script (useful if they already have wz entries since otherwise they're ignored).
NPCS_SCRIPTABLE:
#9200000: Talk to Cody # Cody
9001105: Rescue Gaga! # Grandpa moon bunny

20
pom.xml
View File

@@ -51,7 +51,11 @@
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.10.0</version>
<exclusions></exclusions>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<version>4.1.65.Final</version>
</dependency>
<!-- Logging -->
@@ -87,6 +91,20 @@
<artifactId>js-scriptengine</artifactId>
<version>${graalvm.version}</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.2</version>
</dependency>
</dependencies>
<build>

View File

@@ -21,48 +21,9 @@
*/
/* 9010021 - Wolf Spirit Ryko
@author Ronan
*/
var status;
@author wejrox
*/
function start() {
status = -1;
const YamlConfig = Java.type('config.YamlConfig');
if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) {
cm.sendOk("... I came from distant planes to assist the fight against the #rBlack Magician#k. Right now I search my master, have you seen him?");
cm.dispose();
return;
}
action(1, 0, 0);
}
function action(mode, type, selection) {
if (mode == 1) {
status++;
} else {
cm.dispose();
return;
}
if (status == 0) {
cm.sendNext("Come to me when you want to be reborn again. You currently have a total of #r" + cm.getChar().getReborns() + " #krebirths.");
} else if (status == 1) {
cm.sendSimple("What do you want me to do today: \r\n \r\n #L0##bI want to be rebirthed#l \r\n #L1##bMaybe next time#k#l");
} else if (status == 2) {
if (selection == 0) {
if (cm.getChar().getLevel() == 200) {
cm.sendYesNo("Are you sure you want to be rebirthed?");
} else {
cm.sendOk("You are not level 200, please come back when you hit level 200.");
cm.dispose();
}
} else if (selection == 1) {
cm.sendOk("Ok Bye")
cm.dispose();
}
} else if (status == 3 && type == 1) {
cm.getChar().executeReborn();
cm.sendOk("You have now been reborn. That's a total of #r" + cm.getChar().getReborns() + "#k rebirths");
cm.dispose();
}
cm.sendOk("... I came from distant planes to assist the fight against the #rBlack Magician#k. Right now I search my master, have you seen him?");
cm.dispose();
}

67
scripts/npc/rebirth.js Normal file
View File

@@ -0,0 +1,67 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Rebirth NPC
@author Ronan
@author wejrox
*/
var status;
function start() {
status = -1;
const YamlConfig = Java.type('config.YamlConfig');
if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) {
cm.sendOk("Rebirths aren't enabled on this server, how did you get here?");
cm.dispose();
return;
}
action(1, 0, 0);
}
function action(mode, type, selection) {
if (mode === 1) {
status++;
} else {
cm.dispose();
return;
}
if (status === 0) {
cm.sendNext("Come to me when you want to be reborn again. You currently have a total of #r" + cm.getChar().getReborns() + " #krebirths.");
} else if (status === 1) {
cm.sendSimple("What do you want me to do today: \r\n \r\n #L0##bI want to be reborn!#l \r\n #L1##bNothing for now...#k#l");
} else if (status === 2) {
if (selection === 0) {
if (cm.getChar().getLevel() === 200) {
cm.sendYesNo("Are you sure you want to be reborn?");
} else {
cm.sendOk("You are not level 200, please come back when you hit level 200.");
cm.dispose();
}
} else if (selection === 1) {
cm.sendOk("See you soon!")
cm.dispose();
}
} else if (status === 3 && type === 1) {
cm.getChar().executeReborn();
cm.sendOk("You have now been reborn. That's a total of #r" + cm.getChar().getReborns() + "#k rebirths");
cm.dispose();
}
}

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<>();
}

View File

@@ -1,24 +0,0 @@
package constants.game;
/**
* @brief ScriptableNPCConstants
* @author GabrielSin <gabrielsin@playellin.net>
* @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"));
}};
}

View File

@@ -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();
}
}

View File

@@ -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]);
}
}

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();
}

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);
}

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();
}

View File

@@ -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) {

View File

@@ -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());

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();
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}