Quickslot Bindings
Added quickslot bindings, courtesy of @shavitush.
This commit is contained in:
@@ -16590,6 +16590,16 @@ CREATE TABLE IF NOT EXISTS `queststatus` (
|
|||||||
PRIMARY KEY (`queststatusid`)
|
PRIMARY KEY (`queststatusid`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
|
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
|
||||||
|
|
||||||
|
# quickslot table, by Shavit
|
||||||
|
CREATE TABLE `quickslotkeymapped` (
|
||||||
|
`accountid` INT NOT NULL,
|
||||||
|
`keymap` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`accountid`)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE `quickslotkeymapped`
|
||||||
|
ADD CONSTRAINT `quickslotkeymapped_accountid_fk` FOREIGN KEY (`accountid`) REFERENCES `accounts` (`id`) ON DELETE CASCADE;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `reactordrops` (
|
CREATE TABLE IF NOT EXISTS `reactordrops` (
|
||||||
`reactordropid` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
`reactordropid` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`reactorid` int(11) NOT NULL,
|
`reactorid` int(11) NOT NULL,
|
||||||
|
|||||||
@@ -123,6 +123,8 @@ import tools.exceptions.NotEnabledException;
|
|||||||
import tools.packets.Wedding;
|
import tools.packets.Wedding;
|
||||||
import client.autoban.AutobanManager;
|
import client.autoban.AutobanManager;
|
||||||
import client.creator.CharacterFactoryRecipe;
|
import client.creator.CharacterFactoryRecipe;
|
||||||
|
import client.keybind.MapleKeyBinding;
|
||||||
|
import client.keybind.MapleQuickslotBinding;
|
||||||
import client.inventory.Equip;
|
import client.inventory.Equip;
|
||||||
import client.inventory.Equip.StatUpgrade;
|
import client.inventory.Equip.StatUpgrade;
|
||||||
import client.inventory.Item;
|
import client.inventory.Item;
|
||||||
@@ -175,8 +177,8 @@ import net.server.services.type.ChannelServices;
|
|||||||
import net.server.services.task.channel.FaceExpressionService;
|
import net.server.services.task.channel.FaceExpressionService;
|
||||||
import net.server.services.task.world.CharacterSaveService;
|
import net.server.services.task.world.CharacterSaveService;
|
||||||
import net.server.services.type.WorldServices;
|
import net.server.services.type.WorldServices;
|
||||||
import org.apache.mina.core.session.IoSession;
|
|
||||||
import org.apache.mina.util.ConcurrentHashSet;
|
import org.apache.mina.util.ConcurrentHashSet;
|
||||||
|
import tools.LongTool;
|
||||||
|
|
||||||
public class MapleCharacter extends AbstractMapleCharacterObject {
|
public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||||
private static final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
private static final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||||
@@ -277,6 +279,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
|||||||
private Map<Integer, MapleSummon> summons = new LinkedHashMap<>();
|
private Map<Integer, MapleSummon> summons = new LinkedHashMap<>();
|
||||||
private Map<Integer, MapleCoolDownValueHolder> coolDowns = new LinkedHashMap<>();
|
private Map<Integer, MapleCoolDownValueHolder> coolDowns = new LinkedHashMap<>();
|
||||||
private EnumMap<MapleDisease, Pair<MapleDiseaseValueHolder, MobSkill>> diseases = new EnumMap<>(MapleDisease.class);
|
private EnumMap<MapleDisease, Pair<MapleDiseaseValueHolder, MobSkill>> diseases = new EnumMap<>(MapleDisease.class);
|
||||||
|
public byte[] m_aQuickslotLoaded;
|
||||||
|
public MapleQuickslotBinding m_pQuickslotKeyMapped;
|
||||||
private MapleDoor pdoor = null;
|
private MapleDoor pdoor = null;
|
||||||
private Map<MapleQuest, Long> questExpirations = new LinkedHashMap<>();
|
private Map<MapleQuest, Long> questExpirations = new LinkedHashMap<>();
|
||||||
private ScheduledFuture<?> dragonBloodSchedule;
|
private ScheduledFuture<?> dragonBloodSchedule;
|
||||||
@@ -7496,6 +7500,17 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
|||||||
ret.maplemount.setTiredness(mounttiredness);
|
ret.maplemount.setTiredness(mounttiredness);
|
||||||
ret.maplemount.setActive(false);
|
ret.maplemount.setActive(false);
|
||||||
|
|
||||||
|
try (final PreparedStatement pSelectQuickslotKeyMapped = con.prepareStatement("SELECT keymap FROM quickslotkeymapped WHERE accountid = ?;")) {
|
||||||
|
pSelectQuickslotKeyMapped.setInt(1, ret.getAccountID());
|
||||||
|
|
||||||
|
try (final ResultSet pResultSet = pSelectQuickslotKeyMapped.executeQuery()) {
|
||||||
|
if (pResultSet.next()) {
|
||||||
|
ret.m_aQuickslotLoaded = LongTool.LongToBytes(pResultSet.getLong(1));
|
||||||
|
ret.m_pQuickslotKeyMapped = new MapleQuickslotBinding(ret.m_aQuickslotLoaded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
con.close();
|
con.close();
|
||||||
return ret;
|
return ret;
|
||||||
} catch (SQLException | RuntimeException e) {
|
} catch (SQLException | RuntimeException e) {
|
||||||
@@ -8335,6 +8350,19 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
|||||||
}
|
}
|
||||||
ps.close();
|
ps.close();
|
||||||
|
|
||||||
|
// No quickslots, or no change.
|
||||||
|
boolean bQuickslotEquals = this.m_pQuickslotKeyMapped == null || (this.m_aQuickslotLoaded != null && Arrays.equals(this.m_pQuickslotKeyMapped.m_aQuickslotKeyMapped, this.m_aQuickslotLoaded));
|
||||||
|
if (!bQuickslotEquals) {
|
||||||
|
long nQuickslotKeymapped = LongTool.BytesToLong(this.m_pQuickslotKeyMapped.m_aQuickslotKeyMapped);
|
||||||
|
|
||||||
|
try (final PreparedStatement pInsertStatement = con.prepareStatement("INSERT INTO quickslotkeymapped (accountid, keymap) VALUES (?, ?) ON DUPLICATE KEY UPDATE keymap = ?;")) {
|
||||||
|
pInsertStatement.setInt(1, this.getAccountID());
|
||||||
|
pInsertStatement.setLong(2, nQuickslotKeymapped);
|
||||||
|
pInsertStatement.setLong(3, nQuickslotKeymapped);
|
||||||
|
pInsertStatement.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
itemsWithType = new ArrayList<>();
|
itemsWithType = new ArrayList<>();
|
||||||
for (MapleInventory iv : inventory) {
|
for (MapleInventory iv : inventory) {
|
||||||
for (Item item : iv.list()) {
|
for (Item item : iv.list()) {
|
||||||
@@ -8587,6 +8615,19 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
|||||||
ps.executeBatch();
|
ps.executeBatch();
|
||||||
ps.close();
|
ps.close();
|
||||||
|
|
||||||
|
// No quickslots, or no change.
|
||||||
|
boolean bQuickslotEquals = this.m_pQuickslotKeyMapped == null || (this.m_aQuickslotLoaded != null && Arrays.equals(this.m_pQuickslotKeyMapped.m_aQuickslotKeyMapped, this.m_aQuickslotLoaded));
|
||||||
|
if (!bQuickslotEquals) {
|
||||||
|
long nQuickslotKeymapped = LongTool.BytesToLong(this.m_pQuickslotKeyMapped.m_aQuickslotKeyMapped);
|
||||||
|
|
||||||
|
try (final PreparedStatement pInsertStatement = con.prepareStatement("INSERT INTO quickslotkeymapped (accountid, keymap) VALUES (?, ?) ON DUPLICATE KEY UPDATE keymap = ?;")) {
|
||||||
|
pInsertStatement.setInt(1, this.getAccountID());
|
||||||
|
pInsertStatement.setLong(2, nQuickslotKeymapped);
|
||||||
|
pInsertStatement.setLong(3, nQuickslotKeymapped);
|
||||||
|
pInsertStatement.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
deleteWhereCharacterId(con, "DELETE FROM skillmacros WHERE characterid = ?");
|
deleteWhereCharacterId(con, "DELETE FROM skillmacros WHERE characterid = ?");
|
||||||
ps = con.prepareStatement("INSERT INTO skillmacros (characterid, skill1, skill2, skill3, name, shout, position) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
ps = con.prepareStatement("INSERT INTO skillmacros (characterid, skill1, skill2, skill3, name, shout, position) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
||||||
ps.setInt(1, getId());
|
ps.setInt(1, getId());
|
||||||
@@ -8825,6 +8866,17 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
|||||||
client.announce(MaplePacketCreator.getKeymap(keymap));
|
client.announce(MaplePacketCreator.getKeymap(keymap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendQuickmap() {
|
||||||
|
// send quickslots to user
|
||||||
|
MapleQuickslotBinding pQuickslotKeyMapped = this.m_pQuickslotKeyMapped;
|
||||||
|
|
||||||
|
if (pQuickslotKeyMapped == null) {
|
||||||
|
pQuickslotKeyMapped = new MapleQuickslotBinding(MapleQuickslotBinding.DEFAULT_QUICKSLOTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.announce(MaplePacketCreator.QuickslotMappedInit(pQuickslotKeyMapped));
|
||||||
|
}
|
||||||
|
|
||||||
public void sendMacros() {
|
public void sendMacros() {
|
||||||
// Always send the macro packet to fix a client side bug when switching characters.
|
// Always send the macro packet to fix a client side bug when switching characters.
|
||||||
client.announce(MaplePacketCreator.getMacros(skillMacros));
|
client.announce(MaplePacketCreator.getMacros(skillMacros));
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
You should have received a copy of the GNU Affero General Public License
|
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/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package client;
|
package client.keybind;
|
||||||
|
|
||||||
public class MapleKeyBinding {
|
public class MapleKeyBinding {
|
||||||
private int type, action;
|
private int type, action;
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package client.keybind;
|
||||||
|
|
||||||
|
import tools.data.output.MaplePacketLittleEndianWriter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Shavit
|
||||||
|
*/
|
||||||
|
public class MapleQuickslotBinding
|
||||||
|
{
|
||||||
|
public static final int QUICKSLOT_SIZE = 8;
|
||||||
|
|
||||||
|
public static final byte[] DEFAULT_QUICKSLOTS =
|
||||||
|
{
|
||||||
|
0x2A, 0x52, 0x47, 0x49, 0x1D, 0x53, 0x4F, 0x51
|
||||||
|
};
|
||||||
|
|
||||||
|
public byte[] m_aQuickslotKeyMapped;
|
||||||
|
|
||||||
|
// Initializes quickslot object for the user.
|
||||||
|
// aKeys' length has to be 8.
|
||||||
|
public MapleQuickslotBinding(byte[] aKeys)
|
||||||
|
{
|
||||||
|
if(aKeys.length != QUICKSLOT_SIZE)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException(String.format("aKeys' size should be %d", QUICKSLOT_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.m_aQuickslotKeyMapped = aKeys.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Encode(MaplePacketLittleEndianWriter oPacket)
|
||||||
|
{
|
||||||
|
// Quickslots are default.
|
||||||
|
// The client will skip them and call CQuickslotKeyMappedMan::DefaultQuickslotKeyMap.
|
||||||
|
if(Arrays.equals(this.m_aQuickslotKeyMapped, DEFAULT_QUICKSLOTS))
|
||||||
|
{
|
||||||
|
oPacket.writeBool(false);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
oPacket.writeBool(true);
|
||||||
|
|
||||||
|
for(byte nKey : this.m_aQuickslotKeyMapped)
|
||||||
|
{
|
||||||
|
// For some reason Nexon sends these as integers, similar to CFuncKeyMappedMan.
|
||||||
|
// However there's no evidence any key can be above 0xFF anyhow.
|
||||||
|
// Regardless, we need to encode an integer to avoid an error 38 crash; as CFuncKeyMapped::m_aQuickslotKeyMapped is int[8].
|
||||||
|
oPacket.writeInt(nKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -273,6 +273,7 @@ public final class PacketProcessor {
|
|||||||
registerHandler(RecvOpcode.MOVE_DRAGON, new MoveDragonHandler());
|
registerHandler(RecvOpcode.MOVE_DRAGON, new MoveDragonHandler());
|
||||||
registerHandler(RecvOpcode.OPEN_ITEMUI, new RaiseUIStateHandler());
|
registerHandler(RecvOpcode.OPEN_ITEMUI, new RaiseUIStateHandler());
|
||||||
registerHandler(RecvOpcode.USE_ITEMUI, new RaiseIncExpHandler());
|
registerHandler(RecvOpcode.USE_ITEMUI, new RaiseIncExpHandler());
|
||||||
|
registerHandler(RecvOpcode.CHANGE_QUICKSLOT, new QuickslotKeyMappedModifiedHandler());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,6 +173,7 @@ public enum RecvOpcode {
|
|||||||
DAMAGE_SUMMON(0xB1),
|
DAMAGE_SUMMON(0xB1),
|
||||||
BEHOLDER(0xB2),
|
BEHOLDER(0xB2),
|
||||||
MOVE_DRAGON(0xB5),
|
MOVE_DRAGON(0xB5),
|
||||||
|
CHANGE_QUICKSLOT(0xB7),//CP_QuickslotKeyMappedModified
|
||||||
MOVE_LIFE(0xBC),
|
MOVE_LIFE(0xBC),
|
||||||
AUTO_AGGRO(0xBD),
|
AUTO_AGGRO(0xBD),
|
||||||
FIELD_DAMAGE_MOB(0xBF),
|
FIELD_DAMAGE_MOB(0xBF),
|
||||||
@@ -202,6 +203,7 @@ public enum RecvOpcode {
|
|||||||
MTS_OPERATION(0xFD),
|
MTS_OPERATION(0xFD),
|
||||||
USE_MAPLELIFE(0x100),
|
USE_MAPLELIFE(0x100),
|
||||||
USE_HAMMER(0x104);
|
USE_HAMMER(0x104);
|
||||||
|
|
||||||
private int code = -2;
|
private int code = -2;
|
||||||
|
|
||||||
private RecvOpcode(int code) {
|
private RecvOpcode(int code) {
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ public enum SendOpcode {
|
|||||||
ARIANT_ARENA_SHOW_RESULT(0x9B),
|
ARIANT_ARENA_SHOW_RESULT(0x9B),
|
||||||
PYRAMID_GAUGE(0x9D),
|
PYRAMID_GAUGE(0x9D),
|
||||||
PYRAMID_SCORE(0x9E),
|
PYRAMID_SCORE(0x9E),
|
||||||
|
QUICKSLOT_INIT(0x9F),//LP_QuickslotMappedInit
|
||||||
SPAWN_PLAYER(0xA0),
|
SPAWN_PLAYER(0xA0),
|
||||||
REMOVE_PLAYER_FROM_MAP(0xA1),
|
REMOVE_PLAYER_FROM_MAP(0xA1),
|
||||||
CHATTEXT(0xA2), //0
|
CHATTEXT(0xA2), //0
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ package net.server.channel.handlers;
|
|||||||
|
|
||||||
import constants.game.GameConstants;
|
import constants.game.GameConstants;
|
||||||
import client.MapleClient;
|
import client.MapleClient;
|
||||||
import client.MapleKeyBinding;
|
import client.keybind.MapleKeyBinding;
|
||||||
import client.Skill;
|
import client.Skill;
|
||||||
import client.SkillFactory;
|
import client.SkillFactory;
|
||||||
import client.autoban.AutobanFactory;
|
import client.autoban.AutobanFactory;
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ import client.MapleClient;
|
|||||||
import client.MapleDisease;
|
import client.MapleDisease;
|
||||||
import client.MapleFamily;
|
import client.MapleFamily;
|
||||||
import client.MapleFamilyEntry;
|
import client.MapleFamilyEntry;
|
||||||
import client.MapleKeyBinding;
|
import client.keybind.MapleKeyBinding;
|
||||||
import client.MapleMount;
|
import client.MapleMount;
|
||||||
import client.SkillFactory;
|
import client.SkillFactory;
|
||||||
import client.inventory.Equip;
|
import client.inventory.Equip;
|
||||||
@@ -239,6 +239,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
player.sendKeymap();
|
player.sendKeymap();
|
||||||
|
player.sendQuickmap();
|
||||||
player.sendMacros();
|
player.sendMacros();
|
||||||
|
|
||||||
// pot bindings being passed through other characters on the account detected thanks to Croosade dev team
|
// pot bindings being passed through other characters on the account detected thanks to Croosade dev team
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package net.server.channel.handlers;
|
||||||
|
|
||||||
|
import client.MapleClient;
|
||||||
|
import client.keybind.MapleQuickslotBinding;
|
||||||
|
import net.AbstractMaplePacketHandler;
|
||||||
|
import tools.data.input.SeekableLittleEndianAccessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Shavit
|
||||||
|
*/
|
||||||
|
public class QuickslotKeyMappedModifiedHandler extends AbstractMaplePacketHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c)
|
||||||
|
{
|
||||||
|
// Invalid size for the packet.
|
||||||
|
if(slea.available() != MapleQuickslotBinding.QUICKSLOT_SIZE * Integer.BYTES ||
|
||||||
|
// not logged in-game
|
||||||
|
c.getPlayer() == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] aQuickslotKeyMapped = new byte[MapleQuickslotBinding.QUICKSLOT_SIZE];
|
||||||
|
|
||||||
|
for(int i = 0; i < MapleQuickslotBinding.QUICKSLOT_SIZE; i++)
|
||||||
|
{
|
||||||
|
aQuickslotKeyMapped[i] = (byte) slea.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
c.getPlayer().m_pQuickslotKeyMapped = new MapleQuickslotBinding(aQuickslotKeyMapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package tools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Shavit
|
||||||
|
*/
|
||||||
|
public class LongTool {
|
||||||
|
|
||||||
|
// Converts 8 bytes to a long.
|
||||||
|
public static long BytesToLong(byte[] aToConvert)
|
||||||
|
{
|
||||||
|
if(aToConvert.length != Long.BYTES)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException(String.format("Size of input should be %d", (Long.SIZE / 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
long nResult = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < Long.BYTES; i++)
|
||||||
|
{
|
||||||
|
nResult <<= Byte.SIZE;
|
||||||
|
nResult |= (aToConvert[i] & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a long to 8 bytes.
|
||||||
|
public static byte[] LongToBytes(long nToConvert)
|
||||||
|
{
|
||||||
|
byte[] aBytes = new byte[Long.BYTES];
|
||||||
|
|
||||||
|
for(int i = aBytes.length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
aBytes[i] = (byte) (nToConvert & 0xFF);
|
||||||
|
nToConvert >>= Byte.SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return aBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,7 +43,8 @@ import client.MapleClient;
|
|||||||
import client.MapleDisease;
|
import client.MapleDisease;
|
||||||
import client.MapleFamilyEntitlement;
|
import client.MapleFamilyEntitlement;
|
||||||
import client.MapleFamilyEntry;
|
import client.MapleFamilyEntry;
|
||||||
import client.MapleKeyBinding;
|
import client.keybind.MapleKeyBinding;
|
||||||
|
import client.keybind.MapleQuickslotBinding;
|
||||||
import client.MapleMount;
|
import client.MapleMount;
|
||||||
import client.MapleQuestStatus;
|
import client.MapleQuestStatus;
|
||||||
import client.MapleRing;
|
import client.MapleRing;
|
||||||
@@ -3615,6 +3616,16 @@ public class MaplePacketCreator {
|
|||||||
return mplew.getPacket();
|
return mplew.getPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] QuickslotMappedInit(MapleQuickslotBinding pQuickslot)
|
||||||
|
{
|
||||||
|
final MaplePacketLittleEndianWriter pOutPacket = new MaplePacketLittleEndianWriter();
|
||||||
|
|
||||||
|
pOutPacket.writeShort(SendOpcode.QUICKSLOT_INIT.getValue());
|
||||||
|
pQuickslot.Encode(pOutPacket);
|
||||||
|
|
||||||
|
return pOutPacket.getPacket();
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] getWhisper(String sender, int channel, String text) {
|
public static byte[] getWhisper(String sender, int channel, String text) {
|
||||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||||
mplew.writeShort(SendOpcode.WHISPER.getValue());
|
mplew.writeShort(SendOpcode.WHISPER.getValue());
|
||||||
|
|||||||
Reference in New Issue
Block a user