forked from MagicBane/Server
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
439 lines
15 KiB
439 lines
15 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// 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; |
|
} |
|
|
|
|
|
|
|
}
|
|
|