|
|
|
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
|
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
|
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
|
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
|
|
|
// Magicbane Emulator Project © 2013 - 2022
|
|
|
|
// www.magicbane.com
|
|
|
|
|
|
|
|
|
|
|
|
package engine.net.client;
|
|
|
|
|
|
|
|
import engine.Enum.DispatchChannel;
|
|
|
|
import engine.Enum.GameObjectType;
|
|
|
|
import engine.InterestManagement.WorldGrid;
|
|
|
|
import engine.exception.MsgSendException;
|
|
|
|
import engine.gameManager.*;
|
|
|
|
import engine.job.JobScheduler;
|
|
|
|
import engine.jobs.RefreshGroupJob;
|
|
|
|
import engine.net.Dispatch;
|
|
|
|
import engine.net.DispatchMessage;
|
|
|
|
import engine.net.NetMsgHandler;
|
|
|
|
import engine.net.client.handlers.AbstractClientMsgHandler;
|
|
|
|
import engine.net.client.msg.*;
|
|
|
|
import engine.net.client.msg.chat.AbstractChatMsg;
|
|
|
|
import engine.objects.*;
|
|
|
|
import engine.server.MBServerStatics;
|
|
|
|
import engine.server.world.WorldServer;
|
|
|
|
import engine.session.Session;
|
|
|
|
import engine.util.StringUtils;
|
|
|
|
import org.pmw.tinylog.Logger;
|
|
|
|
|
|
|
|
import java.sql.SQLException;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @author:
|
|
|
|
* @summary: This class is the mainline router for application protocol
|
|
|
|
* messages received by the client.
|
|
|
|
*/
|
|
|
|
|
|
|
|
public class ClientMessagePump implements NetMsgHandler {
|
|
|
|
|
|
|
|
// Instance variable declaration
|
|
|
|
|
|
|
|
private final WorldServer server;
|
|
|
|
|
|
|
|
public ClientMessagePump(WorldServer server) {
|
|
|
|
super();
|
|
|
|
this.server = server;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void ackBankWindowOpened(AckBankWindowOpenedMsg msg, ClientConnection origin) {
|
|
|
|
// According to the Wiki, the client should not send this message.
|
|
|
|
// Log the instance to investigate, and modify Wiki accordingly.
|
|
|
|
Logger.error(msg.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void modifyStat(ModifyStatMsg msg, ClientConnection origin) {
|
|
|
|
|
|
|
|
PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
|
|
|
|
|
|
|
|
if (pc == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int type = msg.getType();
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case MBServerStatics.STAT_STR_ID:
|
|
|
|
pc.addStr(msg.getAmount());
|
|
|
|
break;
|
|
|
|
case MBServerStatics.STAT_DEX_ID:
|
|
|
|
pc.addDex(msg.getAmount());
|
|
|
|
break;
|
|
|
|
case MBServerStatics.STAT_CON_ID:
|
|
|
|
pc.addCon(msg.getAmount());
|
|
|
|
break;
|
|
|
|
case MBServerStatics.STAT_INT_ID:
|
|
|
|
pc.addInt(msg.getAmount());
|
|
|
|
break;
|
|
|
|
case MBServerStatics.STAT_SPI_ID:
|
|
|
|
pc.addSpi(msg.getAmount());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// called when player clicks respawn button
|
|
|
|
private static void respawn(RespawnMsg msg, ClientConnection origin) throws MsgSendException {
|
|
|
|
|
|
|
|
PlayerCharacter sourcePlayer = SessionManager.getPlayerCharacter(origin);
|
|
|
|
|
|
|
|
if (sourcePlayer == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (msg.getObjectType() != sourcePlayer.getObjectType().ordinal() || msg.getObjectID() != sourcePlayer.getObjectUUID()) {
|
|
|
|
Logger.error("Player " + sourcePlayer.getObjectUUID() + " respawning character of id " + msg.getObjectType() + ' '
|
|
|
|
+ msg.getObjectID());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sourcePlayer.isAlive()) {
|
|
|
|
Logger.error("Player " + sourcePlayer.getObjectUUID() + " respawning while alive");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// ResetAfterDeath player
|
|
|
|
sourcePlayer.respawnLock.writeLock().lock();
|
|
|
|
try {
|
|
|
|
sourcePlayer.respawn(true, false, true);
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
Logger.error(e);
|
|
|
|
} finally {
|
|
|
|
sourcePlayer.respawnLock.writeLock().unlock();
|
|
|
|
|
|
|
|
}
|
|
|
|
// Echo ResetAfterDeath message back
|
|
|
|
msg.setPlayerHealth(sourcePlayer.getHealth());
|
|
|
|
// TODO calculate any experience loss before this point
|
|
|
|
msg.setPlayerExp(sourcePlayer.getExp() + sourcePlayer.getOverFlowEXP());
|
|
|
|
Dispatch dispatch = Dispatch.borrow(sourcePlayer, msg);
|
|
|
|
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
|
|
|
|
|
|
|
|
MoveToPointMsg moveMsg = new MoveToPointMsg();
|
|
|
|
moveMsg.setPlayer(sourcePlayer);
|
|
|
|
moveMsg.setStartCoord(sourcePlayer.getLoc());
|
|
|
|
moveMsg.setEndCoord(sourcePlayer.getLoc());
|
|
|
|
moveMsg.setInBuilding(-1);
|
|
|
|
moveMsg.setInBuildingFloor(-1);
|
|
|
|
|
|
|
|
dispatch = Dispatch.borrow(sourcePlayer, moveMsg);
|
|
|
|
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
|
|
|
|
|
|
|
|
MovementManager.sendRWSSMsg(sourcePlayer);
|
|
|
|
|
|
|
|
// refresh the whole group with what just happened
|
|
|
|
JobScheduler.getInstance().scheduleJob(new RefreshGroupJob(sourcePlayer), MBServerStatics.LOAD_OBJECT_DELAY);
|
|
|
|
}
|
|
|
|
|
|
|
|
// called when player types /show
|
|
|
|
private static void show(ShowMsg msg, ClientConnection origin) throws MsgSendException {
|
|
|
|
|
|
|
|
PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
|
|
|
|
|
|
|
|
if (pc == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int targetType = msg.getTargetType();
|
|
|
|
AbstractCharacter tar = null;
|
|
|
|
|
|
|
|
if (targetType == GameObjectType.PlayerCharacter.ordinal())
|
|
|
|
tar = PlayerCharacter.getFromCache(msg.getTargetID());
|
|
|
|
else if (targetType == GameObjectType.NPC.ordinal())
|
|
|
|
tar = NPC.getFromCache(msg.getTargetID());
|
|
|
|
else if (targetType == GameObjectType.Mob.ordinal())
|
|
|
|
tar = Mob.getFromCache(msg.getTargetID());
|
|
|
|
|
|
|
|
if (tar == null || !tar.isAlive() || !tar.isActive())
|
|
|
|
return;
|
|
|
|
|
|
|
|
msg.setUnknown01(pc.getLoc());
|
|
|
|
msg.setUnknown02(pc.getLoc());
|
|
|
|
msg.setRange01(pc.getRange());
|
|
|
|
msg.setUnknown03(tar.getLoc());
|
|
|
|
msg.setUnknown04(tar.getLoc());
|
|
|
|
msg.setRange01(tar.getRange());
|
|
|
|
|
|
|
|
Dispatch dispatch = Dispatch.borrow(pc, msg);
|
|
|
|
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void ViewResourcesMessage(ViewResourcesMessage msg, ClientConnection origin) throws SQLException {
|
|
|
|
|
|
|
|
PlayerCharacter player = SessionManager.getPlayerCharacter(origin);
|
|
|
|
|
|
|
|
if (player == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Guild guild = player.getGuild();
|
|
|
|
City city = guild.getOwnedCity();
|
|
|
|
|
|
|
|
if (city == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Building warehouse = BuildingManager.getBuilding(city.getWarehouseBuildingID());
|
|
|
|
|
|
|
|
if (warehouse == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ViewResourcesMessage vrm = new ViewResourcesMessage(player);
|
|
|
|
vrm.setWarehouseBuilding(warehouse);
|
|
|
|
vrm.setGuild(player.getGuild());
|
|
|
|
vrm.configure();
|
|
|
|
|
|
|
|
Dispatch dispatch = Dispatch.borrow(player, vrm);
|
|
|
|
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
protected static void petAttack(PetAttackMsg msg, ClientConnection conn) throws MsgSendException {
|
|
|
|
|
|
|
|
PlayerCharacter pc = SessionManager.getPlayerCharacter(conn);
|
|
|
|
|
|
|
|
if (pc == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Mob pet = pc.getPet();
|
|
|
|
|
|
|
|
if (pet == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!pet.isAlive())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((pc.inSafeZone())
|
|
|
|
&& (msg.getTargetType() == GameObjectType.PlayerCharacter.ordinal()))
|
|
|
|
return;
|
|
|
|
|
|
|
|
//CombatManager.setAttackTarget(msg, conn);
|
|
|
|
if (msg.getTargetType() == GameObjectType.Building.ordinal()) {
|
|
|
|
conn.getPlayerCharacter().getPet().setCombatTarget(PlayerCharacter.getPlayerCharacter(msg.getTargetID()));
|
|
|
|
}
|
|
|
|
switch (msg.getTargetType()) {
|
|
|
|
case 53: //player character
|
|
|
|
conn.getPlayerCharacter().getPet().setCombatTarget(PlayerCharacter.getPlayerCharacter(msg.getTargetID()));
|
|
|
|
break;
|
|
|
|
case 37://mob
|
|
|
|
conn.getPlayerCharacter().getPet().setCombatTarget(Mob.getMob(msg.getTargetID()));
|
|
|
|
break;
|
|
|
|
case 8://mob
|
|
|
|
conn.getPlayerCharacter().getPet().setCombatTarget(BuildingManager.getBuilding(msg.getTargetID()));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pet.getCombatTarget() == null)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected static void petCmd(PetCmdMsg msg, ClientConnection conn) throws MsgSendException {
|
|
|
|
|
|
|
|
PlayerCharacter pc = SessionManager.getPlayerCharacter(conn);
|
|
|
|
|
|
|
|
if (pc == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Mob pet = pc.getPet();
|
|
|
|
|
|
|
|
if (pet == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!pet.isAlive())
|
|
|
|
return;
|
|
|
|
|
|
|
|
//if (pet.state == STATE.Disabled)
|
|
|
|
// return;
|
|
|
|
|
|
|
|
int type = msg.getType();
|
|
|
|
|
|
|
|
if (type == 1) { //stop attack
|
|
|
|
pet.setCombatTarget(null);
|
|
|
|
pc.setCombat(false);
|
|
|
|
|
|
|
|
} else if (type == 2) { //dismiss
|
|
|
|
pet.dismiss();
|
|
|
|
pc.dismissPet();
|
|
|
|
|
|
|
|
if (pet.isAlive())
|
|
|
|
WorldGrid.updateObject(pet);
|
|
|
|
} else if (type == 3) //toggle assist
|
|
|
|
pet.toggleAssist();
|
|
|
|
else if (type == 5) { //rest
|
|
|
|
boolean sit = (!(pet.isSit()));
|
|
|
|
pet.setSit(sit);
|
|
|
|
|
|
|
|
// cancel effects that break on sit
|
|
|
|
if (pet.isSit())
|
|
|
|
pet.cancelOnSit();
|
|
|
|
|
|
|
|
UpdateStateMsg rwss = new UpdateStateMsg();
|
|
|
|
rwss.setPlayer(pet);
|
|
|
|
DispatchMessage.sendToAllInRange(pet, rwss);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Handle RepairObject Window and RepairObject Requests
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean handleClientMsg(ClientNetMsg msg) {
|
|
|
|
|
|
|
|
if (msg == null) {
|
|
|
|
Logger.error("handleClientMsg", "Recieved null msg. Returning.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ClientConnection origin;
|
|
|
|
Protocol protocolMsg = Protocol.NONE;
|
|
|
|
Session s;
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
// Try registered opcodes first as we take a hatchet to this GodObject
|
|
|
|
|
|
|
|
AbstractClientMsgHandler msgHandler = msg.getProtocolMsg().handler;
|
|
|
|
|
|
|
|
if (msgHandler != null)
|
|
|
|
return msgHandler.handleNetMsg(msg);
|
|
|
|
|
|
|
|
// Any remaining opcodes fall through and are routed
|
|
|
|
// through this ungodly switch of doom.
|
|
|
|
|
|
|
|
origin = (ClientConnection) msg.getOrigin();
|
|
|
|
s = SessionManager.getSession(origin);
|
|
|
|
|
|
|
|
protocolMsg = msg.getProtocolMsg();
|
|
|
|
|
|
|
|
switch (protocolMsg) {
|
|
|
|
/*
|
|
|
|
* Chat
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Simplify by fall through. Route in ChatManager
|
|
|
|
case CHATSAY:
|
|
|
|
case CHATSHOUT:
|
|
|
|
case CHATTELL:
|
|
|
|
case CHATGUILD:
|
|
|
|
case CHATGROUP:
|
|
|
|
case CHATPVP:
|
|
|
|
case CHATIC:
|
|
|
|
case CHATCITY:
|
|
|
|
case CHATINFO:
|
|
|
|
case SYSTEMBROADCASTCHANNEL:
|
|
|
|
case CHATCSR:
|
|
|
|
case SYSTEMCHANNEL:
|
|
|
|
case GLOBALCHANNELMESSAGE:
|
|
|
|
case LEADERCHANNELMESSAGE:
|
|
|
|
ChatManager.handleChatMsg(s, (AbstractChatMsg) msg);
|
|
|
|
break;
|
|
|
|
case READYTOENTER:
|
|
|
|
break;
|
|
|
|
case OPENVAULT:
|
|
|
|
break;
|
|
|
|
case IGNORE:
|
|
|
|
((IgnoreMsg) msg).handleRequest(origin);
|
|
|
|
break;
|
|
|
|
case VIEWRESOURCES:
|
|
|
|
ViewResourcesMessage((ViewResourcesMessage) msg, origin);
|
|
|
|
break;
|
|
|
|
case RAISEATTR:
|
|
|
|
modifyStat((ModifyStatMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case COSTTOOPENBANK:
|
|
|
|
ackBankWindowOpened((AckBankWindowOpenedMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case RESETAFTERDEATH:
|
|
|
|
respawn((RespawnMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case SHOWCOMBATINFO:
|
|
|
|
show((ShowMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case REQUESTTOTRADE:
|
|
|
|
TradeManager.tradeRequest((TradeRequestMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case REQUESTTRADEOK:
|
|
|
|
TradeManager.acceptTradeRequest((AcceptTradeRequestMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case REQUESTTRADECANCEL:
|
|
|
|
TradeManager.rejectTradeRequest((RejectTradeRequestMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case TRADEADDOBJECT:
|
|
|
|
TradeManager.addItemToTradeWindow((AddItemToTradeWindowMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case TRADEADDGOLD:
|
|
|
|
TradeManager.addGoldToTradeWindow((AddGoldToTradeWindowMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case TRADECONFIRM:
|
|
|
|
TradeManager.commitToTrade((CommitToTradeMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case TRADEUNCONFIRM:
|
|
|
|
TradeManager.uncommitToTrade((UncommitToTradeMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case TRADECLOSE:
|
|
|
|
TradeManager.closeTradeWindow((CloseTradeWindowMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case ARCREQUESTTRADEBUSY:
|
|
|
|
TradeManager.invalidTradeRequest((InvalidTradeRequestMsg) msg);
|
|
|
|
break;
|
|
|
|
case TRAINERLIST:
|
|
|
|
WorldServer.trainerInfo((TrainerInfoMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case ARCUNTRAINLIST:
|
|
|
|
WorldServer.refinerScreen((RefinerScreenMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case ARCUNTRAINABILITY:
|
|
|
|
RefineMsg.refine((RefineMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case POWERTARGNAME:
|
|
|
|
PowersManager.summon((SendSummonsRequestMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case ARCSUMMON:
|
|
|
|
PowersManager.recvSummon((RecvSummonsRequestMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case ARCTRACKINGLIST:
|
|
|
|
PowersManager.trackWindow((TrackWindowMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case STUCK:
|
|
|
|
MovementManager.stuck(origin);
|
|
|
|
break;
|
|
|
|
case ARCPETATTACK:
|
|
|
|
petAttack((PetAttackMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case ARCPETCMD:
|
|
|
|
petCmd((PetCmdMsg) msg, origin);
|
|
|
|
break;
|
|
|
|
case CHANNELMUTE:
|
|
|
|
break;
|
|
|
|
case KEEPALIVESERVERCLIENT:
|
|
|
|
break;
|
|
|
|
case UNKNOWN:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CONFIRMPROMOTE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
String ocHex = StringUtils.toHexString(protocolMsg.opcode);
|
|
|
|
Logger.error("Cannot handle Opcode: " + ocHex + " " + protocolMsg.name());
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (MsgSendException | SQLException e) {
|
|
|
|
Logger.error("handler for " + protocolMsg + " failed: " + e);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|