// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // 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; } }